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