Keras and TensorFlow don't export to ONNX directly — you need tf2onnx, a separate converter. It's reliable for most architectures, including the CNN-LSTM pattern that's become popular for retail price-prediction. This article walks through the exact stack that works in 2026, including the TensorFlow version pitfall the MetaQuotes documentation specifically calls out.
What's in this article
The stack that works (TensorFlow 2.10.0)
This matters because newer TensorFlow versions have known installation problems on Windows and intermittent tf2onnx compatibility issues. The combination that's been reliable for MT5-bound exports throughout 2025-2026:
Use a fresh virtual environment with these exact pins. tensorflow>2.10 often fails to install on Windows (the GPU build was dropped from PyPI starting with 2.11). The official MetaQuotes documentation specifically recommends 2.10.0 for this reason.
A working CNN-LSTM in Keras
The canonical architecture for price prediction: a 1D convolution over the time dimension to extract local patterns, followed by an LSTM over the convolution outputs:
Converting to ONNX with tf2onnx
Once the model is saved, tf2onnx converts it from the command line:
The --opset 17 flag is critical — same reason as PyTorch. tf2onnx defaults to a higher opset which MT5 may not load.
If you need it in Python instead of CLI
Normalization: the part everyone forgets
You almost certainly normalized your training data (z-score, min-max, etc.). The model expects normalized inputs at inference time too — including inside the MQL5 EA. The normalization parameters used during training must be saved and re-applied at inference, identically.
The standard pattern: serialize the normalization stats to a separate file alongside the ONNX:
Copy normalization.json next to the .onnx in MQL5\Files\. Read it in OnInit and apply (x - mean) / std to your features before OnnxRun. We document the full MQL5 side in the normalization article.
Validation pass
Same pattern as PyTorch — run the same input through Keras and through ONNX, compare outputs:
If outputs match, ship to MT5. If not, check that you saved with tf.keras.models.save_model(...) in SavedModel format (not HDF5), and that the opset is 17.