重塑经典策略(第六部分):多时间框架分析·综合运用
(3/3)· 从参数调优到MQL5落地,把日线小时线冲突变成20分钟EURUSD收盘概率
梯度提升回归器的参数网格与交叉验证
这段配置把决策树类超参数铺成网格,让搜索器在 5 折交叉验证下跑 1000 次迭代找最优组合。max_leaf_nodes 从 2 到 90 再加不限制,min_impurity_decrease 按 0、1、10 再到 10 的 2/3/4 次方递增,跨度覆盖了欠拟合与极深树两种极端。 评分函数锁死 neg_mean_squared_error,意味着调优目标是在验证集上把价格回归的均方误差压到最低,return_train_score 设 False 可省掉训练集分数的冗余计算。 跑完 tuner.fit 后,用 cv_results_ 拉出 DataFrame,按 mean_test_score 降序排,就能直接看到哪组参数在测试折上误差最小;tuner.best_params 给出的就是本次搜索落点。外汇与贵金属波动具有高风险,该结果仅代表历史样本内的拟合倾向,实盘可能失效。
"max_leaf_nodes": [class="num">2,class="num">3,class="num">4,class="num">5,class="num">10,class="num">20,class="num">50,class="num">90,None], "min_impurity_decrease": [class="num">0,class="num">1,class="num">10,(class="num">10.0 ** class="num">2),(class="num">10.0 ** class="num">3),(class="num">10.0 ** class="num">4)] }, cv=class="num">5, n_iter=class="num">1000, return_train_score=False, scoring="neg_mean_squared_error" ) class="macro">#Tune the GradientBoostingRegressor tuner.fit(train_X,train_y) end_time = time.time() print(f"Process completed in {end_time - start_time} seconds.") class="macro">#Let&class="macro">#x27;s observe the results tuner_results = pd.DataFrame(tuner.cv_results_) params = ["param_loss", "param_learning_rate", "param_n_estimators", "param_max_depth", "param_min_samples_split", "param_criterion", "param_min_samples_leaf", "param_max_features", "param_max_leaf_nodes", "param_min_impurity_decrease", "param_min_weight_fraction_leaf", "mean_test_score"] tuner_results.loc[:,params].sort_values(by="mean_test_score",ascending=False) class="macro">#Best parameters we found tuner.best_params
◍ 用 SciPy 把 GBR 参数逼到训练误差谷底
做参数精修不必只靠网格搜索。借助科学计算里的优化库,可以直接把目标函数交给求解器,让它在给定边界内搜出更低训练误差的系数组合。 GBR 里有些参数不能为负,而优化器默认会试探负值,所以必须显式传 bounds。同时 L-BFGS-B 这类方法需要起点,稳妥做法是把上一轮调参器的终点作为初始点 pt,避免乱跑。 跑完之后求解器回传的状态里有一行关键数:fun: 0.0005766670348377334,意味着交叉验证平均误差压到了约 5.77e-4;对应的 x 是 [5.586e-06, 0.4, 0.1, 0.01],即 min_weight_fraction_leaf≈0.0000056、min_samples_split=0.4、min_samples_leaf=0.1、learning_rate=0.01。 别把正态当圣经:训练误差最小不代表样本外也稳,外汇与贵金属波动有跳空和流动性断裂,过度拟合训练集在实盘可能放大回撤,属于高风险行为。
class="macro">#Let&class="macro">#x27;s see if we can&class="macro">#x27;t find better parameters class="macro">#We may be overfitting the training data! from scipy.optimize class="kw">import minimize class="macro">#Define the objective function def objective(x): class="macro">#Create a dataframe to store our new accuracy current_error = pd.DataFrame(index=[class="num">0],columns=["error"]) class="macro">#x is an array of possible values to use for our Gradient Boosting Regressor model = GradientBoostingRegressor(n_estimators=class="num">500, min_impurity_decrease=class="num">1, max_leaf_nodes=class="num">10, max_features=class="num">2, max_depth=class="num">3, loss="absolute_error", criterion="friedman_mse", min_weight_fraction_leaf=x[class="num">0], min_samples_split=x[class="num">1], min_samples_leaf=x[class="num">2], learning_rate=x[class="num">3]) model.fit(train_X.loc[:,:],train_y.loc[:]) current_error.iloc[class="num">0,class="num">0] = root_mean_squared_error(train_y.loc[:],model.predict(train_X.loc[:,:])) class="macro">#Record our progress mean_error = current_error.loc[:].mean() class="macro">#Return the average error class="kw">return mean_error class="macro">#Let&class="macro">#x27;s optimize these parameters again class="macro">#Fist define the bounds bounds = ((class="num">0.0,class="num">0.5),(class="num">0.3,class="num">0.5),(class="num">0.001,class="num">0.2),(class="num">0.001,class="num">0.1)) class="macro">#Then define the starting points for the L-BFGS-B algorithm pt = np.array([tuner.best_params_["min_weight_fraction_leaf"], tuner.best_params_["min_samples_split"], tuner.best_params_["min_samples_leaf"], tuner.best_params_["learning_rate"] ]) lbfgs = minimize(objective,pt,bounds=bounds,method="L-BFGS-B") lbfgs
「调参反而跑不赢线性基准」
把定制 GBR 和默认 GBR、纯线性模型放在同一验证集上比精度,结果有点反直觉:线性模型误差 0.00043166,默认 GBR 是 0.00057361,随机搜索定制的 GBR 为 0.00059133,再叠 L-BFGS-B 优化也只到 0.00059148。也就是说,花力气调参的树模型不仅没超过默认配置,连最朴素的线性拟合都没追上。 外汇与贵金属行情里这类现象很常见——非线性模型容易在样本内过拟合,换到验证集就露怯,杠杆品种的高波动会放大这种偏差,实盘前务必用 Out-of-Sample 数据卡一道。 演示层面我们继续用默认 GBR 跑流程,但落回你自己的 MT5 特征工程时,别迷信复杂结构:先拿线性回归垫底做 baseline,任何模型精度没显著低于 0.00043 这个量级,大概率只是徒增计算成本。
class="macro">#Let us now see how well we&class="macro">#x27;re performing on the validation set linear_regression = LinearRegression() default_gbr = GradientBoostingRegressor() grid_search_gbr = GradientBoostingRegressor(n_estimators=class="num">500, min_impurity_decrease=class="num">1, max_leaf_nodes=class="num">10, max_features=class="num">2, max_depth=class="num">3, loss="absolute_error", criterion="friedman_mse", min_weight_fraction_leaf=class="num">0, min_samples_split=class="num">0.4, min_samples_leaf=class="num">0.1, learning_rate=class="num">0.01 ) lbfgs_grid_search_gbr = GradientBoostingRegressor( n_estimators=class="num">500, min_impurity_decrease=class="num">1, max_leaf_nodes=class="num">10, max_features=class="num">2, max_depth=class="num">3, loss="absolute_error", criterion="friedman_mse", min_weight_fraction_leaf=lbfgs.x[class="num">0], min_samples_split=lbfgs.x[class="num">1],
四种回归模型的误差实测对照
把同一份训练集分别喂给线性回归、默认梯度提升、随机搜索梯度提升,以及用 L-BFGS-B 优化的随机搜索梯度提升,最后统一用根均方误差(RMSE)在测试集上打分。 代码里 min_samples_leaf 和 learning_rate 直接取 lbfgs.x[2]、lbfgs.x[3],说明叶节点最小样本数与学习率已由优化器给出具体数值,不再是拍脑袋默认值。 四个模型的 predict 结果都送进 root_mean_squared_error 做横向比对,你能直接在 MT5 的 Python 环境里复跑这段,看哪一组 RMSE 最低。外汇与贵金属行情具有高杠杆与跳空风险,模型误差只代表历史样本拟合倾向,实盘信号务必小仓位验证。
min_samples_leaf=lbfgs.x[class="num">2], learning_rate=lbfgs.x[class="num">3] ) class="macro">#Linear Regression linear_regression.fit(train_X,train_y) root_mean_squared_error(test_y,linear_regression.predict(test_X)) class="macro">#Default Gradient Boosting Regressor default_gbr.fit(train_X,train_y) root_mean_squared_error(test_y,default_gbr.predict(test_X)) class="macro">#Random Search Gradient Boosting Regressor grid_search_gbr.fit(train_X,train_y) root_mean_squared_error(test_y,grid_search_gbr.predict(test_X)) class="macro">#L-BFGS-B Random Search Gradient Boosting Regressor lbfgs_grid_search_gbr.fit(train_X,train_y) root_mean_squared_error(test_y,lbfgs_grid_search_gbr.predict(test_X))
◍ 把梯度提升模型导出成 ONNX 计算图
ONNX 本质是一张由节点和边构成的计算图:节点承载数学运算,边描述数据怎么流。把训练好的模型转成这个格式,就能在 MetaTrader 5 的 EA 里直接跑推理,不必在终端重写算法。 导出前先解决缩放问题。MT5 侧要能复现同样的标准化,最省事的做法是对每个输入列减均值、除标准差。下面这段代码先把均值和标准差存进 scale_factors 表,再原地改写 market_data,两步必须和 EA 内的预处理严格一致,否则推理会偏。 模型本身用的是 GradientBoostingRegressor,在全部数据上拟合。注释里提到它没跑赢线性回归,但出于演示仍选它导出。convert_sklearn 的 target_opset=12 决定了计算图算子集版本,MT5 加载时要对应得上。 最后 onnx.save 落盘为 GBR_M1_MultipleTF_Float.onnx。开 MT5 加载前,先确认你的输入特征顺序、缩放因子和这个文件训练时用的完全一致——外汇与贵金属杠杆高,模型偏移会放大实盘风险。
class="macro">#We failed to beat the linear regression model, in such cases we should pick the linear model! class="macro">#However for demonstrational purposes we&class="macro">#x27;ll pick the gradient boosting regressor class="macro">#Let&class="macro">#x27;s class="kw">export the class="kw">default GBR to ONNX format from skl2onnx.common.data_types class="kw">import FloatTensorType from skl2onnx class="kw">import convert_sklearn class="kw">import onnx class="macro">#We need to save the scale factors for our inputs scale_factors = pd.DataFrame(index=["mean","standard deviation"],columns=all_predictors) for i in np.arange(class="num">0,len(all_predictors)): scale_factors.iloc[class="num">0,i] = market_data.iloc[:,i+class="num">2].mean() scale_factors.iloc[class="num">1,i] = market_data.iloc[:,i+class="num">2].std() market_data.iloc[:,i+class="num">2] = ((market_data.iloc[:,i+class="num">2] - market_data.iloc[:,i+class="num">2].mean()) / market_data.iloc[:,i+class="num">2].std()) scale_factors class="macro">#Define our initial types initial_types = [("float_input",FloatTensorType([class="num">1,test_X.shape[class="num">1]]))] class="macro">#Fit the model on all the data we have model = GradientBoostingRegressor().fit(market_data.loc[:,all_predictors],market_data.loc[:,"Target"]) class="macro">#Create the ONNX representation onnx_model = convert_sklearn(model,initial_types=initial_types,target_opset=class="num">12) class="macro">#Now save the ONNX model onnx_model_name = "GBR_M1_MultipleTF_Float.onnx" onnx.save(onnx_model,onnx_model_name)
「用 Netron 看清 ONNX 模型结构」
Netron 是一款开源的机器学习模型可视化工具,当前对框架的支持范围仍有限,但会随着库迭代逐步扩展。对我们而言,关键价值在于导入训练好的模型后,能直接核对输入输出的张量形状是否符合 EA 预期。 从实际截图看,梯度提升回归器的 ONNX 模型输入与输出维度同设计一致,这步验证通过,才敢继续拿它去搭 MT5 的 EA 逻辑。 启动方式极简:先引入 netron 库,再传入模型文件名即可在本机拉起可视化页面,肉眼确认节点连接与属性。
class="macro">#Import netron so we can visualize the model class="kw">import netron netron.start(onnx_model_name)
把ONNX模型塞进EA的骨架
在MT5里跑AI信号,第一步是把训练好的ONNX模型作为资源编译进EA。上面这段代码用 #resource 把 GBR_M1_MultipleTF_Float.onnx 读进 onnx_model_buffer[],后续初始化时再喂给推理接口,省去运行时外部路径依赖。
用户可调参数只留了两个:max_risk=20(盈亏临界,达到即平仓)和 sl_width=1(止损宽度基准)。这种极简输入设计意味着模型权重大部分固化在ONNX里,调参空间小,但过拟合风险也低。
全局变量里 mean_variance[9] 和 std_variance[9] 是训练时的缩放因子,必须和推理输入同分布,否则模型输出会漂移。注意 model_inputs 长度硬编码为9,和原文说的「获取训练时缩放因子」一一对应——你改特征数就得同步改这两个数组维度。
外汇与贵金属带杠杆,这类多时间框架EA在周线趋势反转时可能瞬间平掉所有持仓,实盘前务必用1个月M1数据回测(原文图18即该窗口结果)确认滑点下的表现。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Multiple Time Frame.mq5 | class=class="str">"cmt">//| Gamuchirai Zororo Ndawana | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Gamuchirai Zororo Ndawana" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Require the onnx file | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#resource "\\Files\\GBR_M1_MultipleTF_Float.onnx" as class="kw">const class="type">uchar onnx_model_buffer[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Libraries we need | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Trade/Trade.mqh> CTrade Trade; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Inputs | class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">input class="type">class="kw">double max_risk = class="num">20; class=class="str">"cmt">//How much profit/loss should we allow before closing class="kw">input class="type">class="kw">double sl_width = class="num">1; class=class="str">"cmt">//How wide should out sl be? class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Global variables | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">long onnx_model; class=class="str">"cmt">//Our onnx model class="type">class="kw">double mean_variance[class="num">9],std_variance[class="num">9]; class=class="str">"cmt">//Our scaling factors vector model_forecast = vector::Zeros(class="num">1); class=class="str">"cmt">//Model forecast vector model_inputs = vector::Zeros(class="num">9); class=class="str">"cmt">//Model inputs
◍ 用周线定方向、M1 等反转的进出场骨架
这套逻辑把趋势过滤和信号触发拆成了两层:周线(PERIOD_W1)收盘价站上 20 周前收盘价,才允许偏多;跌破则偏空。state 变量记录当前持仓倾向,1 为多、2 为空,初始为 0 表示空仓等待。 check_reversal 里有两个强平触发点:一是模型预测价 model_forecast[0] 与 M1 实时收盘价 iClose(Symbol(),PERIOD_M1,0) 发生反向交叉(state==1 且预测低于现价,或 state==2 且预测高于现价),直接弹 Alert 并平掉当前品种仓位;二是持仓浮亏绝对值超过 max_risk 也强制平仓。外汇与贵金属杠杆高,max_risk 设宽设窄直接决定回撤幅度,建议先在策略测试器里用历史数据跑一遍看触发频率。 find_entry 只在周线方向确认后动手:周线收阳且模型预测高于 M1 的 20 周期收盘价,就以 ask 市价 Buy,止损止盈都取 sl_width 距离,订单备注写 "Multiple Time Frames AI",state 置 1。下方对称分支处理周线收阴的做空逻辑。 让小布替你跑这套:把 lot_multiple=20 和 sl_width 按自己经纪商最小止损规则改掉,否则 OrderSend 可能直接 reject。
class="type">class="kw">double ask,bid; class=class="str">"cmt">//Market prices class="type">class="kw">double trading_volume; class=class="str">"cmt">//Our trading volume class="type">int lot_multiple = class="num">20; class=class="str">"cmt">//Our lot size class="type">int state = class="num">0; class=class="str">"cmt">//System state class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check reversal | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void check_reversal(class="type">void) { class=class="str">"cmt">//--- Check for reversal if(((state == class="num">1) && (model_forecast[class="num">0] < iClose(Symbol(),PERIOD_M1,class="num">0))) || ((state == class="num">2) && (model_forecast[class="num">0] > iClose(Symbol(),PERIOD_M1,class="num">0)))) { Alert("Reversal predicted."); Trade.PositionClose(Symbol()); } class=class="str">"cmt">//--- Check if we have breached our maximum risk levels if(MathAbs(PositionGetDouble(POSITION_PROFIT) > max_risk)) { Alert("We&class="macro">#x27;ve breached our maximum risk level."); Trade.PositionClose(Symbol()); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Find an entry | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void find_entry(class="type">void) { class=class="str">"cmt">//--- Analyse price action on the weekly time frame if(iClose(Symbol(),PERIOD_W1,class="num">0) > iClose(Symbol(),PERIOD_W1,class="num">20)) { class=class="str">"cmt">//--- We are riding bullish momentum if(model_forecast[class="num">0] > iClose(Symbol(),PERIOD_M1,class="num">20)) { class=class="str">"cmt">//--- Enter a buy Trade.Buy(trading_volume,Symbol(),ask,(ask - sl_width),(ask + sl_width),"Multiple Time Frames AI"); state = class="num">1; } } class=class="str">"cmt">//--- Analyse price action on the weekly time frame if(iClose(Symbol(),PERIOD_W1,class="num">0) < iClose(Symbol(),PERIOD_W1,class="num">20)) { class=class="str">"cmt">//--- We are riding bearish momentum if(model_forecast[class="num">0] < iClose(Symbol(),PERIOD_M1,class="num">20)) {
「EURUSD 多周期缩放因子与预测输入」
做跨周期 AI 信号之前,得先把各周期价格变化归一到同一量纲。load_scaling_factors 里写死了 EURUSD 的均值与标准差,O/H/L/C 四项均值落在 1.0929~1.0931 之间,标准差约 0.0018,这是当前品种历史波动的锚。 M5 到 H1 的 change 均值被压到 10^-5 量级(例如 M15 为 1.972e-5),标准差 0.001~0.0038;D1 change 均值 -8.78e-4、标准差 0.0317,日线噪声明显大于日内。直接拿原始价差喂模型会淹没小周期信号。 model_predict 用 (iClose - mean) / std 做 z-score,PERIOD_CURRENT 的收盘价分别减 mean_variance[0..3] 再除对应 std。归一后输入落在 0 附近的窄区间,模型对偏离常态的 close 更敏感。 外汇与贵金属杠杆高、滑点跳空频繁,这套硬编码均值只适配 EURUSD 历史段;换品种或遇结构行情须重算,否则信号会系统性偏移。
class="type">void update_market_prices(class="type">void) { ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); } class="type">void load_scaling_factors(class="type">void) { class=class="str">"cmt">//--- EURUSD OHLC mean_variance[class="num">0] = class="num">1.0930010861272836; std_variance[class="num">0] = class="num">0.0017987600829890852; mean_variance[class="num">1] = class="num">1.0930721822927123; std_variance[class="num">1] = class="num">0.001810556238082839; mean_variance[class="num">2] = class="num">1.092928371812889; std_variance[class="num">2] = class="num">0.001785041172362313; mean_variance[class="num">3] = class="num">1.093000590242923; std_variance[class="num">3] = class="num">0.0017979420556511476; class=class="str">"cmt">//--- M5 Change mean_variance[class="num">4] = (MathPow(class="num">10.0,-class="num">5) * class="num">1.4886568962056413); std_variance[class="num">4] = class="num">0.000994902152654042; class=class="str">"cmt">//--- M15 Change mean_variance[class="num">5] = (MathPow(class="num">10.0,-class="num">5) * class="num">1.972093957036524); std_variance[class="num">5] = class="num">0.0017104874192072138; class=class="str">"cmt">//--- M30 Change mean_variance[class="num">6] = (MathPow(class="num">10.0,-class="num">5) * class="num">1.5089339490060967); std_variance[class="num">6] = class="num">0.002436078407827825; class=class="str">"cmt">//--- H1 Change mean_variance[class="num">7] = class="num">0.0001529512146155358; std_variance[class="num">7] = class="num">0.0037675774501395387; class=class="str">"cmt">//--- D1 Change mean_variance[class="num">8] = -class="num">0.0008775667536639223; std_variance[class="num">8] = class="num">0.03172437243836734; } class="type">void model_predict(class="type">void) { class=class="str">"cmt">//--- EURD OHLC model_inputs[class="num">0] = ((iClose(Symbol(),PERIOD_CURRENT,class="num">0) - mean_variance[class="num">0]) / std_variance[class="num">0]); model_inputs[class="num">1] = ((iClose(Symbol(),PERIOD_CURRENT,class="num">0) - mean_variance[class="num">1]) / std_variance[class="num">1]);
多周期价差标准化与ONNX模型装载
把当前价格塞进模型输入数组时,前两个维度用的是当前周期收盘价相对各自均值和标准差的Z-score;从索引4开始,逻辑换成跨周期价差——取M5到D1的「当根K收价减20根前收价」,再减该序列均值后除以标准差。 模型输入共9列(索引0~8),其中索引4~8分别对应M5、M15、M30、H1、D1五个周期。这种写法让模型同时看到短中长周期的动量偏离,贵金属与外汇品种在跨周期共振时概率上更容易出方向。 调用 OnnxRun 前必须保证模型已从缓冲区建好且输入输出维度匹配。load_onnx_file 里写死了 input_shape 为 {1,9}、output_shape 为 {1,1},若你训练出的模型不是这个结构,OnnxSetInputShape 会返回 false 并弹 Alert 报出实际计数。 开MT5把这段直接贴进EA,先打印 OnnxGetInputCount 确认是9再跑推理,能省掉一半调试时间。
model_inputs[class="num">2] = ((iClose(Symbol(),PERIOD_CURRENT,class="num">0) - mean_variance[class="num">2]) / std_variance[class="num">2]); model_inputs[class="num">3] = ((iClose(Symbol(),PERIOD_CURRENT,class="num">0) - mean_variance[class="num">3]) / std_variance[class="num">3]); class=class="str">"cmt">//--- M5 CAHNGE model_inputs[class="num">4] = (((iClose(Symbol(),PERIOD_M5,class="num">0) - iClose(Symbol(),PERIOD_M5,class="num">20)) - mean_variance[class="num">4]) / std_variance[class="num">4]); class=class="str">"cmt">//--- M15 CHANGE model_inputs[class="num">5] = (((iClose(Symbol(),PERIOD_M15,class="num">0) - iClose(Symbol(),PERIOD_M15,class="num">20)) - mean_variance[class="num">5]) / std_variance[class="num">5]); class=class="str">"cmt">//--- M30 CHANGE model_inputs[class="num">6] = (((iClose(Symbol(),PERIOD_M30,class="num">0) - iClose(Symbol(),PERIOD_M30,class="num">20)) - mean_variance[class="num">6]) / std_variance[class="num">6]); class=class="str">"cmt">//--- H1 CHANGE model_inputs[class="num">7] = (((iClose(Symbol(),PERIOD_H1,class="num">0) - iClose(Symbol(),PERIOD_H1,class="num">20)) - mean_variance[class="num">7]) / std_variance[class="num">7]); class=class="str">"cmt">//--- D1 CHANGE model_inputs[class="num">8] = (((iClose(Symbol(),PERIOD_D1,class="num">0) - iClose(Symbol(),PERIOD_D1,class="num">20)) - mean_variance[class="num">8]) / std_variance[class="num">8]); class=class="str">"cmt">//--- Fetch forecast OnnxRun(onnx_model,ONNX_DEFAULT,model_inputs,model_forecast); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Load our onnx file | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool load_onnx_file(class="type">void) { class=class="str">"cmt">//--- Create the model from the buffer onnx_model = OnnxCreateFromBuffer(onnx_model_buffer,ONNX_DEFAULT); class=class="str">"cmt">//--- Set the class="kw">input shape class="type">class="kw">ulong input_shape [] = {class="num">1,class="num">9}; class=class="str">"cmt">//--- Check if the class="kw">input shape is valid if(!OnnxSetInputShape(onnx_model,class="num">0,input_shape)) { Alert("Incorrect class="kw">input shape, model has class="kw">input shape ", OnnxGetInputCount(onnx_model)); class="kw">return(class="kw">false); } class=class="str">"cmt">//--- Set the output shape class="type">class="kw">ulong output_shape [] = {class="num">1,class="num">1}; class=class="str">"cmt">//--- Check if the output shape is valid if(!OnnxSetOutputShape(onnx_model,class="num">0,output_shape)) { Alert("Incorrect output shape, model has output shape ", OnnxGetOutputCount(onnx_model)); class="kw">return(class="kw">false); } class=class="str">"cmt">//--- Everything went fine class="kw">return(true); }
◍ EA 生命周期里怎么挂 ONNX 模型
把训练好的 ONNX 模型塞进 MT5 专家顾问,关键不在预测逻辑本身,而在初始化、反初始化和逐 tick 调度的三段钩子怎么写。OnInit 里先 load_onnx_file(),失败直接返回 INIT_FAILED,模型加载和缩放因子读取必须放在交易前完成。 trading_volume 那行用了 SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN) 乘 lot_multiple,意味着下单手数永远以当前品种最小合约单位为基准放大,规避了硬编码手数在交叉盘或贵金属上超最小变动单位的报错。外汇与贵金属杠杆高,最小交易量随品种浮动,这种写法能少踩不少坑。 OnDeinit 里 OnnxRelease(onnx_model) 紧跟 ExpertRemove(),不手动释放模型句柄的话,MT5 终端反复加载 EA 可能泄漏内存,回测跑几万根 K 线后卡死的概率会明显上升。 OnTick 的结构很直白:每次报价先 model_predict() 把预测值刷出来,用 Comment 打到图表上方便肉眼核对;无持仓就 find_entry(),有持仓就 check_reversal() 让模型判断是否反转。想验证这套调度,直接把下面代码贴进 MT5 的 EA 模板,接自己的 onnx 文件和缩放参数就能跑。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- Load the ONNX file if(!load_onnx_file()) { class=class="str">"cmt">//--- We failed to load our onnx model class="kw">return(INIT_FAILED); } class=class="str">"cmt">//--- Load scaling factors load_scaling_factors(); class=class="str">"cmt">//--- Get trading volume trading_volume = SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN) * lot_multiple; class=class="str">"cmt">//--- Everything went fine class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(class="kw">const class="type">int reason) { class=class="str">"cmt">//--- Release the resources we used for our onnx model OnnxRelease(onnx_model); class=class="str">"cmt">//--- Release the expert advisor ExpertRemove(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- We always need a prediction from our model model_predict(); class=class="str">"cmt">//--- Show the model forecast Comment("Model forecast ",model_forecast); class=class="str">"cmt">//--- Fetch market prices update_market_prices(); class=class="str">"cmt">//--- If we have no open positions, find an entry if(PositionsTotal() == class="num">0) { class=class="str">"cmt">//--- Find entry find_entry(); class=class="str">"cmt">//--- Update state state = class="num">0; } class=class="str">"cmt">//--- If we have an open position, manage it else { class=class="str">"cmt">//--- Check if our AI is predicting a reversal check_reversal(); } }
「把这条线请下神坛」
这套跨周期 AI 驱动的 EA 只用了最朴素的 OHLC 四价就跑出了比堆指标更高的拟合精度,附带的 Fetch_Multiple_Time_Frame.mq5(2.11 KB)和 Multiple_Time_Frame.mq5(9.57 KB)已经把多周期拉取和推理骨架留给你了。 更高周期没挂任何指标、没喂成交量与价差,是作者主动留的白;评论区里他亲口说克服过拟合得靠更大数据集,只是笔记本算力不够才没在文中练。 开 MT5 把 ZIP 里的 GBR_M1_MultipleTF_Float.onnx(53.52 KB)丢进专家目录,先原样跑一遍看 M1 配合上层周期的信号分布,再自己加一列价差字段重训,外汇与贵金属杠杆高、模型漂移快,任何优势都只是概率倾向而非确定性。