您应当知道的 MQL5 向导技术(第 51 部分):配以 SAC 的强化学习·综合运用
「用 ONNX 跑强化学习信号推断的坑」
把训练好的 SAC actor 网络塞进 MT5,靠 OnnxRun 做推断前,必须先锁死输入输出张量形状。OnnxSetInputShape 用 ONNX_DEFAULT 配 _actor_in_shape,输出用 OnnxSetOutputShape(m_actor_handle, 0, _actor_out_shape);任何一步返回 false 就直接退出,否则后面推理会拿空矩阵硬跑。 GetOutput 里实际拉数据的是 H1 与 D1 两个周期、各取 shift 0 和 1 的两根 8 长度序列:_in_row 减 _in_row_old、_in_col 减 _in_col_old,得到两段差分状态。环境判定交给 Cql 实例,Environment() 吐出 _e[0] 作为 _x_states[0] 喂给网络。 推断时 _y_mu_logstd 是 6×1 的 matrixf,OnnxRun 用 ONNX_NO_CONVERSION 模式;失败就 Print 错误并返回全 0 的 _out(动作数由 __ACTIONS 定),成功才拆出 _mu 和 _logstd 各 __ACTIONS 长度。外汇和贵金属这种高波动品种,RL 信号只代表概率倾向,实盘前务必在策略测试器用历史数据回测验证形状匹配。
class="kw">return(false); } if(!OnnxSetInputShape(m_actor_handle, ONNX_DEFAULT, _actor_in_shape)) { Print("Actor OnnxSetInputShape error ", GetLastError()); class="kw">return(false); } if(!OnnxSetOutputShape(m_actor_handle, class="num">0, _actor_out_shape)) { Print("Actor OnnxSetOutputShape error ", GetLastError()); class="kw">return(false); } class=class="str">"cmt">//read best weights class=class="str">"cmt">//--- ok class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| This function calculates the next actions to be selected from | class=class="str">"cmt">//| the Reinforcement Learning Cycle. | class=class="str">"cmt">//+------------------------------------------------------------------+ vectorf CSignalSAC::GetOutput() { vectorf _out; class="type">int _load = class="num">1; class="kw">static vectorf _x_states(class="num">1); _out.Init(__ACTIONS); _out.Fill(class="num">0.0); vector _in, _in_row, _in_row_old, _in_col, _in_col_old; if ( _in_row.Init(_load) && _in_row.CopyRates(m_symbol.Name(), PERIOD_H1, class="num">8, class="num">0, _load) && _in_row.Size() == _load && _in_row_old.Init(_load) && _in_row_old.CopyRates(m_symbol.Name(), PERIOD_H1, class="num">8, class="num">1, _load) && _in_row_old.Size() == _load && _in_col.Init(_load) && _in_col.CopyRates(m_symbol.Name(), PERIOD_D1, class="num">8, class="num">0, _load) && _in_col.Size() == _load && _in_col_old.Init(_load) && _in_col_old.CopyRates(m_symbol.Name(), PERIOD_D1, class="num">8, class="num">1, _load) && _in_col_old.Size() == _load ) { _in_row -= _in_row_old; _in_col -= _in_col_old; Cql *QL; Sql _RL; _RL.actions = __ACTIONS;class=class="str">"cmt">//buy, sell, do nothing _RL.environments = __ENVIONMENTS;class=class="str">"cmt">//bullish, bearish, flat QL = new Cql(_RL); vector _e(_load); QL.Environment(_in_row, _in_col, _e); class="kw">delete QL; _x_states[class="num">0] = class="type">class="kw">float(_e[class="num">0]); class="kw">static matrixf _y_mu_logstd(class="num">6, class="num">1); class=class="str">"cmt">//--- run the inference ResetLastError(); if(!OnnxRun(m_actor_handle, ONNX_NO_CONVERSION, _x_states, _y_mu_logstd)) { Print("Actor OnnxConversion error ", GetLastError()); class="kw">return(_out); } else { vectorf _mu(__ACTIONS), _logstd(__ACTIONS); _mu.Fill(class="num">0.0); _logstd.Fill(class="num">0.0);
从网络输出拆出动作均值与对数标准差
这段循环把策略网络吐出的二维数组拆成两条平行向量:前 __ACTIONS 个位置存均值 _mu,偏移 __ACTIONS 之后的位置存对数标准差 _logstd。注意下标 i+__ACTIONS 的写法,它直接把同一张 _y_mu_logstd 矩阵的后半段映射成波动率参数,没有另开缓冲区。 拆完之后立刻丢进 LogProbabilities(_mu, _logstd) 算对数概率,返回值 _out 就是该状态下各动作的高斯对数似然。在 MT5 里把 __ACTIONS 设成你实际下单维度(比如 3 档仓位比例),跑一遍能在专家日志看到 _mu 与 _logstd 的数值范围,借此判断网络是否塌成常数。 外汇与贵金属杠杆高,这类概率输出只代表模型倾向,实盘前务必用历史Tick校验分布漂移。
for(class="type">int i=class="num">0;i<__ACTIONS;i++) { _mu[i] = _y_mu_logstd[i][class="num">0]; _logstd[i] = _y_mu_logstd[i+__ACTIONS][class="num">0]; } _out = LogProbabilities(_mu, _logstd); } } class="kw">return(_out); }
◍ 画得少,看得清
上面这套 SAC 基础实现没依赖张量智代函数库,好处是你能直接看清反向传播为什么慢——多个网络配对训练,且根本没有典型训练数据集可用。 熵项由 alpha 参数调制,作用是在探索时留安全余量;当前 demo 用的是固定 alpha,概率上限制了策略自适应探索的深度。 建议你在 MT5 里把 alpha 改成随目标熵自动调整,附件里 WZ_51.mq5(6.19 KB)和 SignalWZ_51.mqh(10.32 KB)配合三个 ONNX 网络可直接拖进向导编译验证。外汇与贵金属波动剧烈,这类实验策略仅作技术参照,实盘前务必自测风险。