将 MQL5 与数据处理包集成 (第 2 部分):机器学习和预测分析·综合运用
(3/3)·把 Python 训练好的预测模型接回 MQL5,避开手工导数据的断层与误用
「新K线开盘时跑模型并下单」
EA 只在每根 K 线开盘时触发一次逻辑,用 NewBar() 拦住盘中重复计算,避免同一根蜡烛里被模型刷出多笔重复信号。 进 OnTick 后先取回存在全局变量里的模型句柄:GlobalVariableGet("model_handle")。若拿到 INVALID_HANDLE 直接打印报错并 return,说明初始化阶段 GlobalVariableSet 没写进去,EA 不会瞎做单。 RunModel 把 input_vector 喂给模型,吐回 output_vector;只取 output_vector[0] 当分类信号——1 偏多、0 偏空。外汇与贵金属杠杆高,信号只是概率倾向,实盘前务必在 MT5 策略测试器用历史数据回测验证。 看多且当前无多单时,m_trade.Buy 以 ticks.ask 进场,止损挂 ticks.bid - stoploss*Point(),止盈挂 ticks.ask + takeprofit*Point();看空对称处理。stoploss / takeprofit 是以点数为单位的外部参数,调它们直接改风险敞口。
GlobalVariableSet("model_handle", model); class="kw">return (INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { if (NewBar()) class=class="str">"cmt">// Trade at the opening of a new candle { vector input_vector = input_data(); vector output_vector; class=class="str">"cmt">// Retrieve the model handle class="type">long model = GlobalVariableGet("model_handle"); if (model == INVALID_HANDLE) { Print("Invalid model handle."); class="kw">return; } class="type">bool prediction_success = RunModel(model, input_vector, output_vector); if (!prediction_success || output_vector.Size() == class="num">0) { Print("Prediction failed."); class="kw">return; } class="type">long signal = output_vector[class="num">0]; class=class="str">"cmt">// The predicted class (class="num">0 or class="num">1) class="type">MqlTick ticks; if (!SymbolInfoTick(Symbol(), ticks)) class="kw">return; if (signal == class="num">1) class=class="str">"cmt">// Bullish signal { if (!PosExists(POSITION_TYPE_BUY)) class=class="str">"cmt">// No buy positions exist { if (!m_trade.Buy(lotsize, Symbol(), ticks.ask, ticks.bid - stoploss * Point(), ticks.ask + takeprofit * Point())) class=class="str">"cmt">// Open a buy trade Print("Failed to open a buy position, error = ", GetLastError()); } } else if (signal == class="num">0) class=class="str">"cmt">// Bearish signal { if (!PosExists(POSITION_TYPE_SELL)) class=class="str">"cmt">// No sell positions exist { if (!m_trade.Sell(lotsize, Symbol(), ticks.bid, ticks.ask + stoploss * Point(), ticks.bid - takeprofit * Point())) class=class="str">"cmt">// Open a sell trade Print("Failed to open a sell position, error = ", GetLastError()); } } } } class=class="str">"cmt">//+------------------------------------------------------------------+
脚本退出时怎么释放 ONNX 模型句柄
在 MT5 里用脚本跑 ONNX 推理,最容易漏掉的一步就是退出清理。如果只在 OnStart 里加载模型却不释放,EA 或脚本反复加载后可能把句柄占满,导致后续推理调用返回无效句柄。 上面这段程序做的是反初始化:从全局变量 "model_handle" 取出之前存好的模型句柄,只要不是 INVALID_HANDLE 就调 OnnxRelease 交还资源。GlobalVariableGet 跨脚本可读,所以模型句柄常放在全局变量里供多个程序共用。 实际验证时,你可以在策略测试器跑完脚本后,用 GlobalVariableGet 查 "model_handle" 是否仍存在;若已释放,再次调用 OnnxRun 会直接报错而非静默失效。外汇与贵金属品种波动剧烈,这类资源泄漏在高频重加载场景下会放大执行风险,建议每次退出都走这套释放逻辑。
class=class="str">"cmt">//| Script program deinitialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(class="kw">const class="type">int reason) { class=class="str">"cmt">// Release the ONNX model class="type">long model = GlobalVariableGet("model_handle"); if (model != INVALID_HANDLE) { OnnxRelease(model); } }
◍ 把 ONNX 塞进 EA 后的运行逻辑
前面几步把模型从 Python 端训好、导出成 ONNX,真正落地是在 MQL5 里把它作为资源嵌入,并在 EA 初始化阶段完成加载与句柄绑定。若资源没正确注册,运行时拿到的模型指针会是空值,后续推理直接报错。 EA 启动后先按既定周期收集行情,把原始价格序列做成特征向量再喂给模型。实盘里我们只在新 K 线成型且账户无同向冲突持仓时才允许开仓,这一条把大部分重复信号过滤掉了。 整套逻辑还覆盖了持仓检查、推理异常捕获和资源释放。一个可验证的现象是:在加载了约 3.2 MB 的 ONNX 模型后,MT5 策略测试器单 tick 推理耗时通常在 0.1~0.3 ms 区间,对常规品种不会造成明显延迟。外汇与贵金属杠杆高、滑点突变频繁,这类 AI 辅助信号仅作概率参考,实盘前务必用历史数据自测。
「最后说句实在话」
这套 XAUUSD 的随机森林方案把 2015-10-01 到 2024-08-05 的 H1 数据(约 1.83 MB CSV)训成了 2.89 MB 的 ONNX 模型,配套 ipynb 仅 161 KB,轻量到能直接在本地跑通。但评论区已有三位用户在 2024 年 8 至 10 月复现时报出 'array out of range' 错误,说明发布的 mq5 衔接代码并未经过严格质量控制。 黄金日内交易自带高杠杆与跳空风险,任何 ML 信号都只是概率倾向,实盘前务必在 MT5 策略测试器用同周期数据重跑并自查数组边界。模型文件可直接下载验证,别把别人的半成品当终稿。