您应当知道的 MQL5 向导技术(第 60 部分):推理学习(Wasserstein-VAE),配合移动平均线和随机振荡器形态·综合运用
(3/3)· 从 EURUSD 训好的模型到 EURJPY 的轻量复用,VAE 编码器如何把多品种知识压成易存文件
不少交易者以为在 EURUSD 上训好的形态模型换个品种就能直接跑,结果噪声放大、回撤失控。推理学习不是简单复制权重,而是用编码器把已有知识压缩后再适配新市场。这篇收尾把前面监督与强化的铺垫落到跨品种迁移的实操边界。
在 MT5 里用正态方程跑线性回归映射
做推理模型时,我们只从潜在层取回归函数,把缺失输入映射回来,而不是像早先那样把训好的网络导成 ONNX。原因很直接:我们关心的是已经训完的 VAE 输入数据集,手里只剩特征数据,问题就变成基于特征推状态、动作和奖励。 初始化智能系统时要准备配对的特征码、状态码、动作码、奖励码数据集,拿来训一个线性回归模型。之后任何新特征点都能先映射成编码,再在同一模型里反推状态、动作、奖励——这步连接拟合走的是无监督。 MQL5 实现里,类用 m_coefficients / m_intercept 存一维系数,m_coefficients_2d / m_intercept_2d 存二维,矩阵代数批处理提速,单输出多输出都覆盖。Fit 用正态方程 (X'X)^-1 X'y 直接解,乖离时在特征里加一列 1;Fit2d 专用化靠矩阵运算同批处理多个输出。预测走点积,内存预先分配结果向量。 我们加载 EURUSD 日线 2023.01.01–2025.01.01 共两年数据,80% 训练 20% 测试,VAE 出编码后再拟合线性回归。前向漫游测试约在 2025.01.01 前五月,我们拉长到 2024.07.01–2025.01.01 共 6 个月:10 种形态里只有 1、2、5 过测,细看似乎仅形态-1 和形态-5 在两年窗口内有推理能力。状态绑 0–1、动作同区间、奖励 -1 到 +1,但部署时网络输出常溢出绑定,本文不归一化,只提醒生产环境要留意。外汇与贵金属属高风险,这类映射仅作概率参考。
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">// Linear Regressor(unchanged from previous implementation) |</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="keyword">class</span> LinearRegressor { <span class="keyword">class="kw">private</span>: <span class="keyword">vector</span> m_coefficients; <span class="keyword">class="type">class="kw">double</span> m_intercept; <span class="keyword">matrix</span> m_coefficients_2d; <span class="keyword">vector</span> m_intercept_2d; <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">void</span> Fit(<span class="keyword">const</span> <span class="keyword">matrix</span> &X, <span class="keyword">const</span> <span class="keyword">vector</span> &y) { <span class="keyword">class="type">int</span> n = (<span class="keyword">class="type">int</span>)X.Rows(); <span class="keyword">class="type">int</span> p = (<span class="keyword">class="type">int</span>)X.Cols(); <span class="keyword">matrix</span> X_with_bias(n, p + <span class="number">class="num">1</span>); <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i < n; i++) { <span class="keyword">for</span>(<span class="keyword">class="type">int</span> j = <span class="number">class="num">0</span>; j < p; j++) X_with_bias[i][j] = X[i][j]; X_with_bias[i][p] = <span class="number">class="num">1.0</span>; } <span class="keyword">matrix</span> Xt = X_with_bias.Transpose(); <span class="keyword">matrix</span> XtX = Xt.MatMul(X_with_bias); <span class="keyword">matrix</span> XtX_inv = XtX.Inv(); <span class="keyword">vector</span> y_col = y; y_col.Resize(n, <span class="number">class="num">1</span>); <span class="keyword">vector</span> beta = XtX_inv.MatMul(Xt.MatMul(y_col)); m_coefficients = beta; m_coefficients.Resize(p); m_intercept = beta[p]; } <span class="keyword">class="type">void</span> Fit2d(<span class="keyword">const</span> <span class="keyword">matrix</span> &X, <span class="keyword">const</span> <span class="keyword">matrix</span> &Y) { <span class="keyword">class="type">int</span> n = (<span class="keyword">class="type">int</span>)X.Rows(); <span class="comment">class=class="str">"cmt">// Number of samples</span> <span class="keyword">class="type">int</span> p = (<span class="keyword">class="type">int</span>)X.Cols(); <span class="comment">class=class="str">"cmt">// Number of input features</span> <span class="keyword">class="type">int</span> k = (<span class="keyword">class="type">int</span>)Y.Cols(); <span class="comment">class=class="str">"cmt">// Number of output encodings</span> <span class="comment">class=class="str">"cmt">// Add bias term(column of 1s) to X</span> <span class="keyword">matrix</span> X_with_bias(n, p + <span class="number">class="num">1</span>); <span class="keyword">for</span>(<span class="keyword">class="type">int</span> i = <span class="number">class="num">0</span>; i < n; i++) { <span class="keyword">for</span>(<span class="keyword">class="type">int</span> j = <span class="number">class="num">0</span>; j < p; j++) X_with_bias[i][j] = X[i][j]; X_with_bias[i][p] = <span class="number">class="num">1.0</span>; } <span class="comment">class=class="str">"cmt">// Calculate coefficients using normal equation: (X&class="macro">#x27;X)^-class="num">1 X&class="macro">#x27;Y</span> <span class="keyword">matrix</span> Xt = X_with_bias.Transpose(); <span class="keyword">matrix</span> XtX = Xt.MatMul(X_with_bias); <span class="keyword">matrix</span> XtX_inv = XtX.Inv();
◍ 系数拆解与多路预测器的接线方式
这段实现把最小二乘求出的 beta 矩阵拆成两部分:前 p 行是各特征的权重,最后一行是截距。代码里用两个循环分别填进 m_coefficients_2d[j][d] 和 m_intercept_2d[d],维度对齐靠 p(输入特征数)和 k(编码输出数),少一个都会让后续 Predict2d 越界。 Predict2d 的算法很直白:对每个输出维度 d 先取截距,再跑 j 从 0 到 p-1 累加系数乘输入。这种扁平双重循环在 p=20、k=8 的常见编码规模下,单次推断耗时通常在微秒级,MT5 策略里直接嵌不会有明显延迟。 WassersteinVAEPredictors 类只是把四个 LinearRegressor 实例打包:feature 用 Fit2d 吃二维编码,state/action/reward 用一维 Fit。FitPredictors 一次性训完会把 m_predictors_trained 置真,否则 PredictFromFeatures 会 Print 报错并直接 return,不会静默出脏数。 开 MT5 把这段粘进 EA 的 include 区,故意把 m_predictors_trained 改成 false 再调 PredictFromFeatures,能在日志里确认报错路径;接真实行情前先拿历史 bar 的 features 矩阵跑一遍 Fit2d 看 k 维输出是否稳定。
matrix beta = XtX_inv.MatMul(Xt.MatMul(Y)); class=class="str">"cmt">// Split coefficients and intercept m_coefficients_2d.Resize(p, k); class=class="str">"cmt">// Coefficients for each output encodings m_intercept_2d.Resize(k); class=class="str">"cmt">// Intercept for each input feature for(class="type">int j = class="num">0; j < p; j++) { for(class="type">int d = class="num">0; d < k; d++) { m_coefficients_2d[j][d] = beta[j][d]; } } for(class="type">int d = class="num">0; d < k; d++) { m_intercept_2d[d] = beta[p][d]; } } class="type">class="kw">double Predict(const vector &x) { class="kw">return m_intercept + m_coefficients.Dot(x); } vector Predict2d(const vector &X) const { class="type">int p = (class="type">int)X.Size(); class=class="str">"cmt">// Number of input features class="type">int k = (class="type">int)m_intercept_2d.Size(); class=class="str">"cmt">// Number of output encodings vector predictions(k); class=class="str">"cmt">// vector to store predictions for(class="type">int d = class="num">0; d < k; d++) { class=class="str">"cmt">// Initialize with intercept for this output dimension predictions[d] = m_intercept_2d[d]; class=class="str">"cmt">// Add contribution from each feature for(class="type">int j = class="num">0; j < p; j++) { predictions[d] += m_coefficients_2d[j][d] * X[j]; } } class="kw">return predictions; } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">// Wasserstein VAE Predictors Implementation(unchanged) | class=class="str">"cmt">//+------------------------------------------------------------------+ class WassersteinVAEPredictors { class="kw">private: LinearRegressor m_feature_predictor; LinearRegressor m_state_predictor; LinearRegressor m_action_predictor; LinearRegressor m_reward_predictor; class="type">bool m_predictors_trained; class="kw">public: WassersteinVAEPredictors() : m_predictors_trained(false) {} class="type">void FitPredictors(const matrix &features, const vector &states, const vector &actions, const vector &rewards, const matrix &encodings) { m_feature_predictor.Fit2d(features, encodings); m_state_predictor.Fit(encodings, states); m_action_predictor.Fit(encodings, actions); m_reward_predictor.Fit(encodings, rewards); m_predictors_trained = true; } class="type">void PredictFromFeatures(const vector &y, vector &z) { if(!m_predictors_trained) { Print("Error: Predictors not trained yet"); class="kw">return;
「推理前向如何把隐变量转成多空信号」
WVAE 信号类的 Infer 方法承担推理前向:先把行情特征塞进对应周期的 VAE 实例,再经编码、预测两步拿到 state / action / reward 三个标量,最后按持仓方向做偏置裁剪。 代码里 Index 只接 1、2、5 三种值,分别对应 m_vae_1 / m_vae_2 / m_vae_5;若预测器未训练会直接 Print 报错并返回,不会往下跑。这意味着你挂 EA 前必须确认对应周期模型已 fit 完,否则推理向量全为 0。 多头分支里,state 大于 0.5 才保留:先减 0.5 再乘 2.0 做区间拉伸,若 action 小于 0 则 state 强制归零。空头对称处理,state 小于 0.5 时减 0.5 乘 -2.0,action 大于 0 同样清零。 返回的 _inference 是长度 3 的 vector,下标 0 为处理后的 state、1 为 action、2 为 reward。外汇与贵金属波动剧烈、杠杆风险高,这套推理输出只代表模型倾向,实盘前务必在 MT5 策略测试器用历史数据核验各周期命中率。
vector CSignal_WVAE::Infer(class="type">int Index, class="type">ENUM_POSITION_TYPE T) { vectorf _f = Get(Index, m_time.GetData(X()), m_close, m_ma, m_ma_lag, m_sto); vector _features; _features.Init(_f.Size()); _features.Fill(class="num">0.0); for(class="type">int i = class="num">0; i < class="type">int(_f.Size()); i++) { _features[i] = _f[i]; } class=class="str">"cmt">// Make a prediction vector _encodings; _encodings.Init(__ENCODINGS); _encodings.Fill(class="num">0.0); class="type">class="kw">double _state = class="num">0.0, _action = class="num">0.0, _reward = class="num">0.0; if(Index == class="num">1) { m_vae_1.PredictFromFeatures(_features, _encodings); m_vae_1.PredictFromEncodings(_encodings, _state, _action, _reward); } else if(Index == class="num">2) { m_vae_2.PredictFromFeatures(_features, _encodings); m_vae_2.PredictFromEncodings(_encodings, _state, _action, _reward); } else if(Index == class="num">5) { m_vae_5.PredictFromFeatures(_features, _encodings); m_vae_5.PredictFromEncodings(_encodings, _state, _action, _reward); } vector _inference; _inference.Init(class="num">3); _inference[class="num">0] = _state; _inference[class="num">1] = _action; _inference[class="num">2] = _reward; class=class="str">"cmt">// if(T == POSITION_TYPE_BUY) { if(_state > class="num">0.5) { _inference[class="num">0] -= class="num">0.5; _inference[class="num">0] *= class="num">2.0; if(_action < class="num">0.0) { _inference[class="num">0] = class="num">0.0; } } else { _inference[class="num">0] = class="num">0.0; } } else if(T == POSITION_TYPE_SELL) { if(_state < class="num">0.5) { _inference[class="num">0] -= class="num">0.5; _inference[class="num">0] *= -class="num">2.0; if(_action > class="num">0.0) { _inference[class="num">0] = class="num">0.0; } } else { _inference[class="num">0] = class="num">0.0; } } class="kw">return(_inference); }
一点提醒
把移动平均线和随机振荡器喂给监督学习只是起点,实盘里强化学习会接着滚动推进,但两类知识目前各存各的,缺一个能统一收纳的层。推理学习不一定立刻落地,却可能补上这块拼图,它不重复前面两种训练方式,而是尝试把已学形态压缩成可复用的判断。 附带文件里 wz_60.mq5 仅 7.11 KB,SignalWZ_60.mqh 有 633.05 KB,三个 VAE 模型各 144.49 KB,在 MT5 用向导汇编时头文件必须齐,模型文件对 EA 运行并非必需。外汇与贵金属波动剧烈,这类实验性系统只适合在策略测试器里验证,实盘前先想清楚样本外失效的概率。