A moving-average crossover EA is the canonical "starter strategy" — cheap to backtest, easy to understand, and famously mediocre on its own because it generates many false signals in ranging markets. This article adds a LightGBM trend filter that the EA consults before acting on a crossover signal: only take the trade if the filter predicts the trend will persist.

The base EA (without filter)

Standard: buy when fast MA crosses above slow MA, sell when it crosses below. We'll keep this exact signal generation — we just wrap it with a yes/no decision from the ONNX model.

Training the trend filter

For each historical MA crossover signal, label whether it would have been profitable (1) or not (0). Use a fixed-horizon exit rule, e.g., 50 bars after entry. Features:

train_filter.py
import lightgbm as lgb from onnxmltools import convert_lightgbm from onnxmltools.convert.common.data_types import FloatTensorType # X: features at crossover. y: 1 if 50-bar return was profitable. params = {"objective": "binary", "learning_rate": 0.05, "num_leaves": 31, "max_depth": 6} booster = lgb.train(params, lgb.Dataset(X, label=y), num_boost_round=200) n_features = X.shape[1] onx = convert_lightgbm( booster, initial_types=[("input", FloatTensorType([None, n_features]))], target_opset=17, ) with open("trend_filter.onnx", "wb") as f: f.write(onx.SerializeToString())

Using the filter in MQL5

ma_cross_with_filter.mq5
#resource "models\trend_filter.onnx" as uchar FilterModel[] input double InpFilterThreshold = 0.55; long hFilter; double FilterProbability() { matrixf in(1, 12); vectorf out(2); // fill 12 features as training did, then: OnnxRun(hFilter, ONNX_NO_CONVERSION, in, out); return out[1]; // probability of class 1 (profitable) } void OnTick() { // ... existing crossover detection ... if(BullishCrossover && FilterProbability() > InpFilterThreshold) PlaceBuy(); if(BearishCrossover && FilterProbability() > InpFilterThreshold) PlaceSell(); }

What to expect from the filter