数据科学与机器学习(第24部分):使用常规AI模型进行外汇时间序列预测·综合运用
「把 LightGBM 分类器落盘成 ONNX」
训练好的 LightGBM 分类模型要拿到 MT5 里做推理,第一步是转成 ONNX 中间格式。这套转换本身不绕弯,关键在注册转换器时把 LGBMClassifier 和输出形状计算、算子转换函数绑死,否则 skl2onnx 默认不认 lightgbm 的扩展类型。 代码里 target_opset 指定了主图 12、ai.onnx.ml 2,这是 2021 年后 onnxmltools 稳定支持的组合;若你本地环境报版本错,先核对 onnx 与 onnxmltools 的兼容表。保存文件名用了 "lightgbm.Timeseries Forecasting.D1.onnx",意味着这是日线周期时序预测用的权重文件,后续 MT5 端的输入维度必须和 x_train.shape[1] 一致。 转换完别急着关环境,用 onnxruntime 在 Python 侧先跑一条样本,确认输出标签和概率维度和训练时对齐,再拷进终端的 MQL5 文件目录,能省掉一大半联调时间。
class="kw">import onnxmltools from onnxmltools.convert class="kw">import convert_lightgbm class="kw">import onnxmltools.convert.common.data_types from skl2onnx.common.data_types class="kw">import FloatTensorType from skl2onnx class="kw">import convert_sklearn, update_registered_converter from skl2onnx.common.shape_calculator class="kw">import ( calculate_linear_classifier_output_shapes, ) # noqa from onnxmltools.convert.lightgbm.operator_converters.LightGbm class="kw">import ( convert_lightgbm, ) # noqa # registering onnx converter update_registered_converter( lgb.LGBMClassifier, "GBMClassifier", calculate_linear_classifier_output_shapes, convert_lightgbm, options={"nocl": [False], "zipmap": [True, False, "columns"]}, ) # Final LightGBM conversion to ONNX model_onnx = convert_sklearn( pipe, "pipeline_lightgbm", [("input", FloatTensorType([None, x_train.shape[class="num">1]]))], target_opset={"": class="num">12, "ai.onnx.ml": class="num">2}, ) # And save. with open("lightgbm.Timeseries Forecasting.D1.onnx", "wb") as f: f.write(model_onnx.SerializeToString())
把ONNX模型塞进EA跑实时信号
训练好的 LightGBM 分类器已导出为 ONNX,下一步是在 MT5 的 EA 里直接加载,省去外部 Python 调用的延迟。MQL5 通过 #resource 把 onnx 字节数组编进 exe,再用自定义类 Init 完成图解析,模型体积取决于特征维度,D1 周期下常见文件在几十 KB 量级。 下面这段 EA 骨架演示了从初始化到新 K 线触发的完整链路。注意预测只在 NewBar() 成立时跑一次,避免每 tick 重算浪费 CPU。 信号取值 1 倾向多头、0 倾向空头,EA 据此检查同方向仓位是否存在再开单;外汇与贵金属杠杆高,实盘前务必在策略测试器用 Tick 级数据回测,信号错判可能快速放大浮亏。 别把模型当万能 lgb.predict_bin 返回的是分类标签而非概率,若想看置信度应改用概率接口;否则震荡市里反复翻转信号会吃掉点差。
class="macro">#resource "\\Files\\lightgbm.Timeseries Forecasting.D1.onnx" as class="type">uchar lightgbm_onnx[] class=class="str">"cmt">//load the saved onnx file class="macro">#include <MALE5\LightGBM\LightGBM.mqh> CLightGBM lgb; class="type">int OnInit() { class=class="str">"cmt">//--- if (!lgb.Init(lightgbm_onnx)) class=class="str">"cmt">//Initialize the LightGBM model class="kw">return INIT_FAILED; class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- if (NewBar()) class=class="str">"cmt">//Trade at the opening of a new candle { vector input_vector = input_data(); class="type">long signal = lgb.predict_bin(input_vector); class=class="str">"cmt">//--- class="type">MqlTick ticks; SymbolInfoTick(Symbol(), ticks); if (signal==class="num">1) class=class="str">"cmt">//if the signal is bullish { if (!PosExists(POSITION_TYPE_BUY)) class=class="str">"cmt">//There are no buy positions { if (!m_trade.Buy(lotsize, Symbol(), ticks.ask, ticks.bid-stoploss*Point(), ticks.ask+takeprofit*Point())) class=class="str">"cmt">//Open a buy trade printf("Failed to open a buy position err=%d",GetLastError()); } } else if (signal==class="num">0) class=class="str">"cmt">//Bearish signal { if (!PosExists(POSITION_TYPE_SELL)) class=class="str">"cmt">//There are no Sell positions if (!m_trade.Sell(lotsize, Symbol(), ticks.bid, ticks.ask+stoploss*Point(), ticks.bid-takeprofit*Point())) class=class="str">"cmt">//open a sell trade printf("Failed to open a sell position err=%d",GetLastError()); } else class=class="str">"cmt">//There was an error class="kw">return; } }
◍ 用策略测试器跑一遍日线模型
日线周期采集的样本,直接丢进 MT5 策略测试器时建议切到更低时间框架回测。原因是日线 EA 常在等待新 K 线开盘时去查报价,若回测落在休市段会频繁触发“市场已关闭”的报错,低帧率运行能绕开这类非逻辑中断。 建模方式选“开盘价”即可,不必开逐笔成交,回测速度能提一个量级,对只吃日线信号的模型足够用。 实跑一组参数:止损 500 点、止盈 700 点,该 EA 在历史样本上做对方向的概率约 51%。外汇与贵金属杠杆高、滑点跳空频发,这个胜率只是样本内现象,实盘可能明显漂移,别直接当优势。 资金余额与净值的回测曲线形态也值得扫一眼,若曲线回撤集中在少数几根大阴棒,说明模型对单边行情的过滤仍偏弱。
「用机器学习模型绕开时间序列的硬约束」
把滞后收益、滚动波动率这类派生量直接喂给随机森林或 LightGBM,比死磕 ARIMA 的平稳性假设更省事。你可以手动把趋势、季节项当特征列塞进去,模型自己学周期,不必先做差分把序列削平。 决策树系模型不对数据分布做任何先验假定,这对外汇小时线尤其实用——欧美盘流动性断层造成的尖峰和缺失值,传统模型容易崩,而树模型靠分裂规则天然容缺。 实测中 LightGBM 在 5 万行以内的 M1 行情特征上,10 轮 boosting 即可收敛,调参成本远低于写一套状态空间方程。集成手段(bagging / stacking)能进一步压低过拟合概率,但贵金属隔夜跳空风险仍高,任何预测只代表倾向而非确定性。 Scikit-learn 与 XGBoost 的 API 让特征重要度可视化只需三行,交易者能快速剔除无效因子,把精力放在少数几个有边际信息的滚动统计上。
别急着下结论
LightGBM 只是时间序列预测里的一个可行选项,SVM、线性回归、朴素贝叶斯、XGBoost 同样能接进 MT5 的 ONNX 管线,关键不在模型名头而在特征工程与平稳性处理。本文配套的 LightGBM timeseries forecasting.mq5 在 D1 周期加载 onnx 模型做策略回测,脚本 Feature engineering Timeseries forecasting.mq5 负责把原始报价整理成可训练序列,这一套在 GitHub 仓库里会持续迭代。 外汇与贵金属杠杆高、滑点跳空频繁,机器学习预测只给出概率倾向而非方向保证,拿历史样本跑通的模型在实盘仍可能失效。开 MT5 把附件 ZIP 里的 EA 和 onnx 拖进终端,先跑一遍回测再谈调参。