如何在 MQL5 中集成 ONNX 模型的示例·综合运用
◍ 把 ONNX 预测信号接进开平仓逻辑
下面这段把模型输出的分类直接映射成 MT5 的下单动作。预测类别 PRICE_DOWN 对应卖单,PRICE_UP 对应买单,其余情况 signal 保持 WRONG_VALUE 不下单,避免模型模糊时硬做。 静态向量 output_data(3) 承接三个类别的推理得分,用 ArgMax 取最大下标当预测类。归一化时先减均值 m 再除标准差 s,样本来自最近 sample_size 根 K 线的收盘价,这一步若 CopyRates 拉不到数据直接 return(-1),说明行情源断流要先排查。 开仓函数里有个实打实的限制:必须 TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) 为真才允许 PositionOpen,自动交易开关没开就完全不跑。手数用外部参数 InpLots,发单价格按信号方向取 BID 或 ASK,滑点参数填 0。 平仓逻辑更激进一些:持多单遇到 PRICE_DOWN、持空单遇到 PRICE_UP 就先 PositionClose(_Symbol,3) 市价平掉,紧接着调 CheckForOpen() 反手。外汇和贵金属杠杆高,这种模型翻转即反手的做法在震荡市可能连续挨巴掌,概率上更怕假突破。 枚举 EnModels 留了 USE_FIRST_MODEL 和 USE_SECOND_MODEL 两个档,意味着同一 EA 可切换不同 ONNX 权重,实盘前建议在策略测试器里分别跑一遍看哪套在近期品种上回撤更小。
class="kw">static vectorf output_data(class="num">3); class=class="str">"cmt">// vector to get result class=class="str">"cmt">//--- request last bars if(!input_data.CopyRates(_Symbol,_Period,COPY_RATES_CLOSE,class="num">1,sample_size)) class="kw">return(-class="num">1); class=class="str">"cmt">//--- get series Mean class="type">class="kw">float m=input_data.Mean(); class=class="str">"cmt">//--- get series Std class="type">class="kw">float s=input_data.Std(); class=class="str">"cmt">//--- normalize prices input_data-=m; input_data/=s; class=class="str">"cmt">//--- run the inference if(!OnnxRun(handle,ONNX_NO_CONVERSION,input_data,output_data)) class="kw">return(-class="num">1); class=class="str">"cmt">//--- evaluate prediction class="kw">return(class="type">int(output_data.ArgMax())); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check for open position conditions | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CheckForOpen(class="type">void) { ENUM_ORDER_TYPE signal=WRONG_VALUE; class=class="str">"cmt">//--- check signals if(ExtPredictedClass==PRICE_DOWN) signal=ORDER_TYPE_SELL; class=class="str">"cmt">// sell condition else { if(ExtPredictedClass==PRICE_UP) signal=ORDER_TYPE_BUY; class=class="str">"cmt">// buy condition } class=class="str">"cmt">//--- open position if possible according to signal if(signal!=WRONG_VALUE && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) ExtTrade.PositionOpen(_Symbol,signal,InpLots, SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ? SYMBOL_BID:SYMBOL_ASK), class="num">0,class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check for close position conditions | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CheckForClose(class="type">void) { class="type">bool bsignal=class="kw">false; class=class="str">"cmt">//--- position already selected before class="type">long type=PositionGetInteger(POSITION_TYPE); class=class="str">"cmt">//--- check signals if(type==POSITION_TYPE_BUY && ExtPredictedClass==PRICE_DOWN) bsignal=true; if(type==POSITION_TYPE_SELL && ExtPredictedClass==PRICE_UP) bsignal=true; class=class="str">"cmt">//--- close position if possible if(bsignal && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) { ExtTrade.PositionClose(_Symbol,class="num">3); class=class="str">"cmt">//--- open opposite CheckForOpen(); } } enum EnModels { USE_FIRST_MODEL, class=class="str">"cmt">// Use first model only USE_SECOND_MODEL, class=class="str">"cmt">// Use second model only
「双模型投票怎么在 MT5 里落地」
EA 里用枚举控制模型参与方式,默认 USE_BOTH_MODELS 表示两个模型都跑;手数通过 InpLots=1.0 写死,实盘前建议按账户净值重算,外汇与贵金属杠杆高,重仓可能直接爆仓。
Predict() 先按开关算各自分类:开双模型或仅第一模型时调 PredictPrice,开双模型或仅第二模型时调 PredictPriceMovement。两个句柄与样本数 SAMPLE_SIZE1/2 来自前段训练产物。
投票逻辑在 switch 里:单模型直接采用;双模型时两分类一致才输出该类,否则 ExtPredictedClass=-1 代表信号冲突。你在 MT5 策略测试器把 InpModels 切到 USE_FIRST_MODEL 对比,可能发现冲突周里单边模型胜率反而更高。
enum EnModels { USE_FIRST_MODEL, class=class="str">"cmt">// Use first model USE_SECOND_MODEL, class=class="str">"cmt">// Use second model USE_BOTH_MODELS class=class="str">"cmt">// Use both models }; input EnModels InpModels = USE_BOTH_MODELS; class=class="str">"cmt">// Models using input class="type">class="kw">double InpLots = class="num">1.0; class=class="str">"cmt">// Lots amount to open position class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Voting classification | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void Predict(class="type">void) { class=class="str">"cmt">//--- evaluate first model if(InpModels==USE_BOTH_MODELS || InpModels==USE_FIRST_MODEL) ExtPredictedClass1=PredictPrice(ExtHandle1,SAMPLE_SIZE1); class=class="str">"cmt">//--- evaluate second model if(InpModels==USE_BOTH_MODELS || InpModels==USE_SECOND_MODEL) ExtPredictedClass2=PredictPriceMovement(ExtHandle2,SAMPLE_SIZE2); class=class="str">"cmt">//--- check predictions class="kw">switch(InpModels) { case USE_FIRST_MODEL : ExtPredictedClass=ExtPredictedClass1; class="kw">break; case USE_SECOND_MODEL : ExtPredictedClass=ExtPredictedClass2; class="kw">break; case USE_BOTH_MODELS : if(ExtPredictedClass1==ExtPredictedClass2) ExtPredictedClass=ExtPredictedClass1; else ExtPredictedClass=-class="num">1; } }
多模型调用的硬上限与复现坑
在 MT5 里用 ONNX 做推理,单 EA 同时挂的模型数有硬顶:最多 256 个。前面两模型叠加的示范只是开胃菜,真要上三个以上,就得换面向对象的写法去管生命周期和推理调度,不然内存和调用秩序都会乱。 回测结果对不上,往往不是代码错。有用户用相同日期和设置跑,结果却不同;社区里提到两个常见源:一是交易服务器不是 MetaQuotes-Demo,时区和品种报价节奏会偏;二是模型权重初始化没做固定随机种子,重新训练后权重起点不同,输出自然漂移。外汇和贵金属本身高杠杆、高波动,这类偏差会直接放大成账户曲线的分歧。 下面这段是某次重训 EURUSD D1 模型的终端输出,能直接看到无 GPU 环境下降级到 CPU、以及 50 轮训练后测试集 mae 停在 0.758 附近。想复现的人,先确认自己也是 CPU 版 TF、且用的 MetaQuotes 演示账户,否则数字可能对不上。
D:\MT5 Demo1\MQL5\Experts\article_12433\Python>python ONNX.eurusd.D1.class="num">10.Training.py class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">38.169418: W tensorflow/stream_executor/platform/class="kw">default/dso_loader.cc:class="num">64] Could not load dynamic library &class="macro">#x27;cudart64_110.dll&class="macro">#x27;; dlerror: cudart64_110.dll not found class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">38.169664: I tensorflow/stream_executor/cuda/cudart_stub.cc:class="num">29] Ignore above cudart dlerror if you do not have a GPU set up on your machine. data path to save onnx model class="num">100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| class="num">5187/class="num">5187 [class="num">00:class="num">00<class="num">00:class="num">00, class="num">6068.93it/s] class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">40.434910: W tensorflow/stream_executor/platform/class="kw">default/dso_loader.cc:class="num">64] Could not load dynamic library &class="macro">#x27;nvcuda.dll&class="macro">#x27;; dlerror: nvcuda.dll not found class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">40.435070: W tensorflow/stream_executor/cuda/cuda_driver.cc:class="num">263] failed call to cuInit: UNKNOWN ERROR(class="num">303) class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">40.437138: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:class="num">169] retrieving CUDA diagnostic information for host: WIN-SSPXX7BO0B0 class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">40.437323: I tensorflow/stream_executor/cuda/cuda_diagnostics.cc:class="num">176] hostname: WIN-SSPXX7BO0B0 class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">40.437676: I tensorflow/core/platform/cpu_feature_guard.cc:class="num">193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library(oneDNN) to use the following CPU instructions in performance-critical operations: AVX AVX2 To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags. Epoch class="num">1/class="num">50 class="num">111/class="num">111 - 1s - loss: class="num">1.6160 - mae: class="num">0.9378 - val_loss: class="num">2.7602 - val_mae: class="num">1.3423 - lr: class="num">0.0010 - 1s/epoch - 12ms/step Epoch class="num">2/class="num">50 class="num">111/class="num">111 - 0s - loss: class="num">1.4932 - mae: class="num">0.8952 - val_loss: class="num">2.4339 - val_mae: class="num">1.2412 - lr: class="num">0.0010 - 287ms/epoch - 3ms/step ... class="num">111/class="num">111 - 0s - loss: class="num">1.2812 - mae: class="num">0.8145 - val_loss: class="num">1.2598 - val_mae: class="num">0.8142 - lr: class="num">1.0000 e-class="num">06 - 366ms/epoch - 3ms/step Epoch class="num">50/class="num">50 class="num">111/class="num">111 - 0s - loss: class="num">1.3030 - mae: class="num">0.8203 - val_loss: class="num">1.2604 - val_mae: class="num">0.8143 - lr: class="num">1.0000 e-class="num">06 - 365ms/epoch - 3ms/step class="num">33/class="num">33 [==============================] - 0s 1ms/step - loss: class="num">1.1542 - mae: class="num">0.7584 test_loss=class="num">1.154 test_mae=class="num">0.758
◍ 把这条线请下神坛
上面这段日志来自一次 EURUSD D1 周期的 TensorFlow 训练跑批,模型最终落地为 model.eurusd.D1.10.onnx 与 model.eurusd.D1.63.onnx 两个文件。训练到第 300 轮时,训练集 accuracy 停在 0.6770,而验证集 val_accuracy 只有 0.4789,测试集 test_accuracy 进一步滑到 0.479。 差距说明一个事实:在日线级别用这类浅层网络做方向分类,过拟合倾向很明显,验证与测试表现接近随机猜。外汇和贵金属本身高波动、高杠杆,直接拿 0.48 命中率的信号去开仓,亏损概率天然占优。 小布盯盘接这条线,目的不是神化 AI 预测,而是把日志、onnx 模型和 MT5 实盘价差摆在一起,让你自己跑一遍看差距。能验证、能证伪,才算工具,不是神谕。
class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">57.480814: I tensorflow/core/grappler/devices.cc:class="num">66] Number of eligible GPUs(core count >= class="num">8, compute capability >= class="num">0.0): class="num">0 class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">57.481315: I tensorflow/core/grappler/clusters/single_machine.cc:class="num">358] Starting new session class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">57.560110: I tensorflow/core/grappler/devices.cc:class="num">66] Number of eligible GPUs(core count >= class="num">8, compute capability >= class="num">0.0): class="num">0 class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">57.560380: I tensorflow/core/grappler/clusters/single_machine.cc:class="num">358] Starting new session class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">57.611678: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:class="num">354] MLIR V1 optimization pass is not enabled saved model to model.eurusd.D1.class="num">10.onnx class="num">24/class="num">24 - class="num">0 s - loss: class="num">0.6618 - accuracy: class="num">0.6736 - val_loss: class="num">0.8993 - val_accuracy: class="num">0.4759 - lr: class="num">4.1746 e-class="num">05 - class="num">37 ms/epoch - class="num">2 ms/step Epoch class="num">300/class="num">300 class="num">24/class="num">24 - class="num">0 s - loss: class="num">0.6531 - accuracy: class="num">0.6770 - val_loss: class="num">0.8997 - val_accuracy: class="num">0.4789 - lr: class="num">4.1746 e-class="num">05 - class="num">39 ms/epoch - class="num">2 ms/step class="num">11/class="num">11 [==============================] - class="num">0 s class="num">682 us/step - loss: class="num">0.8997 - accuracy: class="num">0.4789 test_loss=class="num">0.900 test_accuracy=class="num">0.479 class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">19.838160: I tensorflow/core/grappler/devices.cc:class="num">66] Number of eligible GPUs(core count >= class="num">8, compute capability >= class="num">0.0): class="num">0 class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">19.838516: I tensorflow/core/grappler/clusters/single_machine.cc:class="num">358] Starting new session class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">19.872285: I tensorflow/core/grappler/devices.cc:class="num">66] Number of eligible GPUs(core count >= class="num">8, compute capability >= class="num">0.0): class="num">0 class="num">2023-class="num">11-class="num">19 class="num">18:class="num">07:class="num">19.872584: I tensorflow/core/grappler/clusters/single_machine.cc:class="num">358] Starting new session saved model to model.eurusd.D1.class="num">63.onnx