您应当知道的 MQL5 向导技术(第 51 部分):配以 SAC 的强化学习(基础篇)
📘

您应当知道的 MQL5 向导技术(第 51 部分):配以 SAC 的强化学习(基础篇)

第 1/3 篇

用 MQL5 向导搭一个带 SAC 的强化学习框架

MQL5 向导从 build 2875 起支持通过元数据标签把强化学习智能体直接挂进 EA 模板,省去手写交易循环。本文演示的是把 Soft Actor-Critic(SAC)算法接进向导生成骨架,让策略在仿真环境里自己调仓位权重。 实际跑通时,向导默认生成的 OnTick 只负责信号转发,训练回路要写在独立的 CSacAgent 类里,并用 #property script_show_inputs 暴露超参。下面这段代码是向导产出的最小可编译骨架,删掉了无关绘图。 外汇与贵金属杠杆高,强化学习在样本外容易过拟合,实盘前务必用 MT5 策略测试器做至少 3 个月 Tick 级回测再考虑。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                      Expert Advisor generated by Wizard
class=class="str">"cmt">//|                                                      with SAC reinforcement
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property script_show_inputs
class="macro">#include <Trade/Trade.mqh>
CTrade trade;
class=class="str">"cmt">//--- SAC agent placeholder
class CSacAgent {
class="kw">public:
   class="type">class="kw">double Act(class="type">class="kw">double &state[]) { class="kw">return class="num">0.0; }
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick() {
   class="type">class="kw">double state[class="num">4] = {class="num">0,class="num">0,class="num">0,class="num">0};
   CSacAgent agent;
   class="type">class="kw">double lot = agent.Act(state);
   if(lot > class="num">0.0) trade.Buy(lot, _Symbol);
}
class=class="str">"cmt">//+------------------------------------------------------------------+

「双评论者压制 Q 值高估的实现骨架」

柔性参与者评论者(SAC)在 MT5 里落地时,网络结构是三块:两个评论者加一个参与者。两个评论者都吃环境状态和动作,各自吐出一个 Q 值预估,训练参与者时取两者的最小值去调制损失。这种取最小值的做法直接针对 Q 学习里常见的正向偏差——单网络容易把回报估高,双网络并行能把这类误差拉回一点。 参与者网络只收环境状态坐标,输出均值向量和对数标准差向量,再喂进高斯过程得到动作的概率分布。因为分布是随机的,探索性比确定性策略强,但也意味着训练逻辑和评论者完全两路。 下面这段是 MQL5 里算对数概率的核心函数,用重参数化技巧把随机性拆出来: //+------------------------------------------------------------------+ // Function to compute the Gaussian probability distribution and log // probabilities //+------------------------------------------------------------------+ vectorf CSignalSAC::LogProbabilities(vectorf &Mean, vectorf &Log_STD) { vectorf _log_probs; // Compute standard deviations from log_std vectorf _std = exp(Log_STD); // Sample actions and compute log probabilities float _z = float(rand() % USHORT_MAX / USHORT_MAX); // Generate N(0, 1) sample // Sample action using reparameterization trick: action = mean + std * N(0, 1) vectorf _actions = Mean + (_std * _z); // Compute log probability of the sampled action vectorf _variance = _std * _std; vectorf _diff = _actions - Mean; _log_probs = -0.5f * (log(2.0f * M_PI * _variance) + (_diff * _diff) / _variance); return(_log_probs); } 逐行看:exp(Log_STD) 把对数标准差还原成标准差;rand()%USHORT_MAX/USHORT_MAX 生成一个近似标准正态的噪声 _z;Mean + (_std * _z) 就是重参数化采样,动作变成可微形式;最后那行套的是单维高斯对数概率公式,常数 0.5 和 2π 是解析解自带的,不是拍脑袋。 在 MT5 里把这段代码挂上参与者网络后,你能直接改 Log_STD 的初始偏置来调探索强度——偏置给大,早期动作更散,金价或欧元波动剧烈时可能更快跳出局部策略,但过散会拖慢收敛,外汇和贵金属杠杆高,参数乱动容易放大回撤风险。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">// Function to compute the Gaussian probability distribution and log
class=class="str">"cmt">// probabilities
class=class="str">"cmt">//+------------------------------------------------------------------+
vectorf CSignalSAC::LogProbabilities(vectorf &Mean, vectorf &Log_STD)
{  vectorf _log_probs;
   class=class="str">"cmt">// Compute standard deviations from log_std
   vectorf _std = exp(Log_STD);
   class=class="str">"cmt">// Sample actions and compute log probabilities
   class="type">class="kw">float _z = class="type">class="kw">float(rand() % USHORT_MAX / USHORT_MAX); class=class="str">"cmt">// Generate N(class="num">0, class="num">1) sample
   class=class="str">"cmt">// Sample action class="kw">using reparameterization trick: action = mean + std * N(class="num">0, class="num">1)
   vectorf _actions = Mean + (_std * _z);
   class=class="str">"cmt">// Compute log probability of the sampled action
   vectorf _variance = _std * _std;
   vectorf _diff = _actions - Mean;
   _log_probs = -class="num">0.5f * (log(class="num">2.0f * M_PI * _variance) + (_diff * _diff) / _variance);
   class="kw">return(_log_probs);
}

◍ SAC 里的熵与温度怎么牵着策略走

SAC 的参与者网络输出的是随机策略,本质由概率分布参数化,本文实现用的是高斯分布。分布的熵刻画了智能体在某状态下可能出手的分散程度:熵高对应广撒网式探索,熵低则偏向判定性利用,目的就是压住过早收敛到次优解的风险。 温度参数 α 直接称量熵项在目标里的权重。我们当前写死为 0.5,对数概率函数里已体现;α 大鼓励探索,小则催熟利用,0.5 算是折中。若放任不管,Log-STD 会直接拽着熵跑——它管动作分布的扩散,高 Log-STD 意味更宽更不确定的出手,低 Log-STD 把样本死死收在 μ 附近,变相增加利用。 为稳训练,常把 log(σ) 用“z”参数夹在一段区间里,免得熵爆表或塌缩。自动熵调谐则另开一路:用目标熵 H 动态修 α,更新式含学习率 λ 与当前策略动作期望的对数概率。λ 控 α 追偏离的速度,H 在离散动作(买/卖/持)时按动作维度给,连续动作可缩放。 自动调 α 的好处是智能体随环境变自己改探索-利用配比,省去手调;代价是熵项常驻会带来额外计算开销,且探索过泛会以牺牲已知高回报策略为代价拖慢效率。外汇与贵金属市场信息不全、跳空频繁,高熵策略理论上更抗过拟合,但实盘仍属高风险,参数平衡需自己在 MT5 回测里盯曲线。

连续控制场景里 SAC 凭什么压过 DQN

在机械臂固定并搬运物体到指定位置的任务里,关节扭矩与角度调整落在连续动作空间。DQN 原生适配离散动作,硬扩到连续域会让可用离散动作数随维度指数膨胀,训练开销陡增且效率差。它还靠 ε-贪婪平衡探索利用,在离散化高维空间里往往失灵,且自带高估偏差,奖励波动大时训练易抖。 SAC 直接用随机策略输出连续动作分布,省掉离散化环节,机械臂控制更平顺精准。其三个网络分工:Actor 生成概率加权的动作分布提高效率并缓过早探索;双 Critic 取 Q 值最小值回传 Actor,抑制过估、稳住训练。熵项加自动温度 alpha 调整进一步促探索。 Open AI Gym 的 Reacher、Fetch 公开样例显示,DQN 因离散输出与弱探索,策略常陷次优、臂姿生硬;SAC 凭随机策略出平滑精确动作,任务完成更快、碰撞少,对物体或目标位移的适应更好。外汇与贵金属行情虽非机械臂,但连续止损仓位调节也属连续控制,这类算法迁移需警惕过拟合与实盘高风险。

「把 SAC 网络从 Python 搬进 MT5 的实操路径」

在 MT5 里跑深度学习模型,MQL5 原生只支持 OpenCL,面对 Python 那套 TensorFlow / PyTorch 高级库仍显单薄。但 Python 社区产出的 SAC(软演员-评论者)类架构,已经能在实际训练效率上拉开差距,外汇与贵金属交易者若想自建模型,绕不开这条跨语言链路。 手工实现 SAC 时,得在 TensorFlow/Keras 里分别定义三个主网络:演员网络输出高斯采样的随机策略,两个评论者网络做孪生 Q 学习以压住高估偏差,熵正则固定 alpha=0.5 管探索。训练数据用旧版 MT5 库导入,按 2:1 切分训练集与测试集,在 for 循环里模拟交易环境——这套写法效率偏低,但反向传播中要拿评论者 Q 值去训演员,标准 fit 函数用不了。 训完把三个网络导成 ONNX 才是关键一步。ONNX 作为开放神经网络交换标准,能让 PyTorch、SciKit-Learn 训出的模型在 MQL5 里直接调,不必重写逻辑。编译单个 ex5 把 ONNX 当资源嵌进去,交易者只管一个文件。导出时务必记清每层输入输出形状,MQL5 初始化 ONNX 句柄就靠这个;tf2onnx 可行,onnxmltools、skl2onnx 也都能用。 第二种路数是用 TensorFlow 自带的张量智代库,初始化连环境带智能体一起包了,比手写 for 循环更贴近强化学习本体。下一篇再拆它,现在先记住:跨到 ONNX 这步不做实,模型进 MT5 就是空中楼阁,贵金属杠杆品种的高波动更会放大形状录错带来的静默失效。

MQL5 / C++
class="kw">import numpy as np
class="kw">import pandas as pd
from sklearn.model_selection class="kw">import train_test_split
class="kw">import tensorflow as tf
print("Num GPUs Available: ", len(tf.config.list_physical_devices(&class="macro">#x27;GPU&class="macro">#x27;)))
from tensorflow.keras class="kw">import optimizers
from tensorflow.keras.layers class="kw">import Input
from tensorflow.keras class="kw">import layers
from tensorflow class="kw">import keras
class="kw">import tf2onnx
class="kw">import onnx
class="kw">import os
# Define the actor network
def ActorNetwork(state_dim, action_dim, hidden_units=class="num">256):
    """
    Creates a simple SAC actor network.
    
    :param state_dim: The dimension of the state space.
    :param action_dim: The dimension of the action space.
    :param hidden_units: The number of hidden units in each layer.
    :class="kw">return: A Keras model representing the actor network.
    """
    # Input layer
    state_input = layers.Input(shape=(state_dim, ))
    
    # Hidden layers(Dense layers with ReLU activation)
    x = layers.Dense(hidden_units, activation=&class="macro">#x27;relu&class="macro">#x27;)(state_input)
    x = layers.Dense(hidden_units, activation=&class="macro">#x27;relu&class="macro">#x27;)(x)
    
    # Output layer: output means(mu) and log standard deviation(log_std) for Gaussian distribution
    
    output_size = action_dim + action_dim
    stacked_mean_logs = layers.Dense(output_size)(x)
    
    # Create the model
    actor_model = tf.keras.Model(inputs=state_input, outputs=stacked_mean_logs)
    
    class="kw">return actor_model
# Define the critic network
def CriticNetwork(state_dim, action_dim, hidden_units=class="num">256):
    """
    Creates a simple SAC critic network(Q-value approximation).
    
    :param state_dim: The dimension of the state space.
    :param action_dim: The dimension of the action space.
    :param hidden_units: The number of hidden units in each layer.
    :class="kw">return: A Keras model representing the critic network.
    """
    
    input_size = state_dim + action_dim

常见问题

可用现成的向导式生成器产出基础结构,再补双评论者与熵温度参数即可,不必从零手搓网络。
DQN 天然适配离散动作,连续动作会爆空间;SAC 用随机策略加最大熵,样本效率和稳定性都更优。
小布可辅助做模型格式核对与参数映射提醒,把重复搬运和校验交给它,你只管调策略逻辑。
温度太大策略接近随机乱动,太小则退化为确定性策略、探索枯竭;一般从 0.2 起做网格微调。
取两路 Q 估计的较小值作为目标,可截断乐观偏差;实现时确保目标网络延迟更新即可见效。