scikit-learn covers the "everything classical" half of the ML world — logistic regression, random forests, SVM, gradient boosting, k-means. All of these can be exported to ONNX with skl2onnx, the official scikit-learn-to-ONNX converter maintained by ONNX itself.
Install
install
pip install scikit-learn==1.5.0
pip install skl2onnx==1.17.0
pip install onnx==1.16.0
pip install "numpy<2.0"
Minimal example: random forest
export_rf.py
from sklearn.ensemble import RandomForestClassifier
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
clf = RandomForestClassifier(n_estimators=100, max_depth=8)
clf.fit(X_train, y_train)
n_features = X_train.shape[1]
initial_types = [("input", FloatTensorType([None, n_features]))]
onx = convert_sklearn(
clf, initial_types=initial_types, target_opset=17,
options={id(clf): {"zipmap": False}}, # IMPORTANT — see dtypes section
)
with open("rf.onnx", "wb") as f:
f.write(onx.SerializeToString())
Converting a full Pipeline (with normalization)
If you wrapped your model in a Pipeline with a StandardScaler (recommended), skl2onnx exports both as part of the same ONNX graph. The MQL5 side then receives raw features and the model normalizes them internally:
export_pipeline.py
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
pipe = Pipeline([
("scaler", StandardScaler()),
("clf", LogisticRegression()),
])
pipe.fit(X_train, y_train)
initial_types = [("input", FloatTensorType([None, X_train.shape[1]]))]
onx = convert_sklearn(
pipe, initial_types=initial_types, target_opset=17,
options={id(pipe): {"zipmap": False}},
)
with open("pipe.onnx", "wb") as f:
f.write(onx.SerializeToString())
This is the cleanest way to handle normalization — bake it into the graph. The alternative (saving stats to JSON and re-applying in MQL5) is more code but slightly more flexible. Both work.
Dtypes: the int64 trap
scikit-learn's classifiers output two things: the predicted class (an integer) and the class probabilities (floats). By default, skl2onnx packages the probabilities as a ZipMap — a sequence of class-to-probability dictionaries. MQL5's ONNX runtime doesn't handle ZipMap well.
The fix is in the example above: options={id(clf): {"zipmap": False}}. This makes the output a plain 2D float matrix (shape [batch, n_classes]), which MQL5 reads cleanly. Without this flag, you'll get cryptic errors at OnnxRun time.
The other dtype issue: classifiers output an int64 prediction tensor. MQL5 reads it with vector<long> (signed 64-bit) rather than the more familiar float types. Check with Netron which output index is the class and which is the probability — you'll usually want the probability matrix only.
Loading in MQL5
sklearn model in MQL5
#resource "models\rf.onnx" as uchar RFModel[]
long hRF = OnnxCreateFromBuffer(RFModel, ONNX_USE_CPU_ONLY);
// Random forest: input (1, n_features), output prob (1, n_classes)
const long in_shape[] = {1, 10};
const long out_shape[] = {1, 2}; // binary classification
OnnxSetInputShape(hRF, 0, in_shape);
OnnxSetOutputShape(hRF, 1, out_shape); // output 1 = probabilities