Como Exportar Modelo Python (PyTorch + Sklearn) para ONNX e Usar no MT5
Tutorial passo-a-passo, sem firula: você sai daqui com dois modelos ONNX funcionais (um Sklearn Random Forest, um PyTorch LSTM), prontos pra serem consumidos no Expert Advisor MT5. Pré-requisito: Python 3.10+ e MT5 instalado. Se você ainda não tem o panorama geral, comece pelo guia pillar ONNX no MT5.
Setup do ambiente Python
# Bibliotecas essenciais pip install MetaTrader5 numpy pandas scikit-learn # Exportadores ONNX pip install onnx onnxruntime onnxmltools skl2onnx # Para PyTorch (CPU) pip install torch torchvision # Validação pip install matplotlib
O pacote MetaTrader5 permite que o Python leia candles direto do terminal MT5 aberto — sem export CSV nem gambiarra.
Parte 1 — Random Forest (Sklearn) → ONNX
Caso de uso: classificar a direção do próximo candle (sobe/desce) baseado em features técnicas dos últimos N candles. Random Forest é leve, treina rápido e funciona bem como baseline.
Coletar dados do MT5
Conecta no terminal aberto e puxa histórico do ativo desejado.
import MetaTrader5 as mt5
import pandas as pd
from datetime import datetime, timedelta
mt5.initialize()
simbolo = "EURUSD"
timeframe = mt5.TIMEFRAME_H1
n_barras = 20000
rates = mt5.copy_rates_from_pos(simbolo, timeframe, 0, n_barras)
df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
df.to_csv("dados_eurusd_h1.csv", index=False)
print(f"✓ {len(df)} candles salvos")
mt5.shutdown()
Gerar features e target
Features: retornos dos últimos 10 candles. Target: 1 se próximo candle subiu, 0 caso contrário.
import pandas as pd
import numpy as np
df = pd.read_csv("dados_eurusd_h1.csv")
# Retorno percentual
df['ret'] = df['close'].pct_change()
# Features: últimos 10 retornos
for i in range(1, 11):
df[f'ret_lag_{i}'] = df['ret'].shift(i)
# Target: 1 se próximo candle subiu
df['target'] = (df['ret'].shift(-1) > 0).astype(int)
df = df.dropna()
feature_cols = [f'ret_lag_{i}' for i in range(1, 11)]
X = df[feature_cols].values.astype(np.float32)
y = df['target'].values
print(f"X shape: {X.shape} | y balance: {y.mean():.3f}")
np.save("X.npy", X)
np.save("y.npy", y)
Treinar Random Forest
Split temporal (sem shuffle — ordem importa em séries temporais).
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
X = np.load("X.npy")
y = np.load("y.npy")
# Split temporal 80/20
split = int(len(X) * 0.8)
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]
model = RandomForestClassifier(
n_estimators=200,
max_depth=8,
min_samples_leaf=20,
random_state=42,
n_jobs=-1
)
model.fit(X_train, y_train)
pred = model.predict(X_test)
print(f"Accuracy: {accuracy_score(y_test, pred):.3f}")
print(classification_report(y_test, pred))
import joblib
joblib.dump(model, "modelo_rf.pkl")
⚠️ Realidade do accuracy
Se sua accuracy passar de 55% num split temporal honesto, é ótimo. Se passar de 65%, desconfie de data leakage. Acima de 80% em série temporal financeira é quase sempre bug, não talento.
Exportar para ONNX
Usa skl2onnx com tipo de entrada definido.
import joblib
import numpy as np
from skl2onnx import convert_sklearn
from skl2onnx.common.data_types import FloatTensorType
model = joblib.load("modelo_rf.pkl")
# Define o shape da entrada: 10 features
initial_type = [('input', FloatTensorType([None, 10]))]
onnx_model = convert_sklearn(
model,
initial_types=initial_type,
target_opset=15
)
with open("modelo_rf.onnx", "wb") as f:
f.write(onnx_model.SerializeToString())
print("✓ modelo_rf.onnx criado")
# Validação rápida com onnxruntime
import onnxruntime as ort
sess = ort.InferenceSession("modelo_rf.onnx")
X_teste = np.load("X.npy")[-5:].astype(np.float32)
pred_onnx = sess.run(None, {'input': X_teste})
print(f"Predições ONNX (últimas 5): {pred_onnx[0]}")
Parte 2 — LSTM (PyTorch) → ONNX
Mesmo problema, modelo mais sofisticado. LSTM aprende dependências temporais que Random Forest ignora.
import numpy as np
import torch
import torch.nn as nn
X = np.load("X.npy") # (N, 10)
y = np.load("y.npy")
# Reshape para sequência: (batch, seq_len=10, features=1)
X_seq = X.reshape(-1, 10, 1)
split = int(len(X) * 0.8)
X_train = torch.tensor(X_seq[:split], dtype=torch.float32)
y_train = torch.tensor(y[:split], dtype=torch.long)
X_test = torch.tensor(X_seq[split:], dtype=torch.float32)
y_test = torch.tensor(y[split:], dtype=torch.long)
class LSTMTrader(nn.Module):
def __init__(self, input_size=1, hidden_size=32, num_layers=2):
super().__init__()
self.lstm = nn.LSTM(input_size, hidden_size, num_layers,
batch_first=True, dropout=0.2)
self.fc = nn.Linear(hidden_size, 2)
def forward(self, x):
out, _ = self.lstm(x)
out = self.fc(out[:, -1, :]) # último timestep
return out
model = LSTMTrader()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
# Training loop simples
for epoch in range(20):
optimizer.zero_grad()
out = model(X_train)
loss = criterion(out, y_train)
loss.backward()
optimizer.step()
if epoch % 5 == 0:
with torch.no_grad():
acc = (model(X_test).argmax(1) == y_test).float().mean()
print(f"Epoch {epoch} | loss {loss.item():.4f} | val acc {acc:.3f}")
# === EXPORT PARA ONNX ===
model.eval()
dummy_input = torch.randn(1, 10, 1)
torch.onnx.export(
model,
dummy_input,
"modelo_lstm.onnx",
input_names=['input'],
output_names=['output'],
dynamic_axes={
'input': {0: 'batch'},
'output': {0: 'batch'}
},
opset_version=15
)
print("✓ modelo_lstm.onnx exportado")
Parte 3 — Consumir no Expert Advisor MT5
Os arquivos .onnx vão para a pasta MQL5\Files da instalação MT5. No EA, carrega via OnnxCreate:
// Carrega arquivo da pasta Files
long handle = OnnxCreate("modelo_rf.onnx", ONNX_DEFAULT);
if(handle == INVALID_HANDLE)
{
Print("Erro: ", GetLastError());
return INIT_FAILED;
}
// Define shape: batch=1, features=10
const long input_shape[] = {1, 10};
const long output_shape[] = {1};
OnnxSetInputShape(handle, 0, input_shape);
OnnxSetOutputShape(handle, 0, output_shape);
// Prepara features dos últimos 10 candles
matrix input(1, 10);
for(int i = 0; i < 10; i++)
{
double close_atual = iClose(_Symbol, PERIOD_CURRENT, i + 1);
double close_ant = iClose(_Symbol, PERIOD_CURRENT, i + 2);
input[0][i] = (float)((close_atual - close_ant) / close_ant);
}
// Roda inferência
vector output(1);
OnnxRun(handle, ONNX_DEFAULT, input, output);
int direcao = (int)output[0]; // 0 = baixa, 1 = alta
Print("Predição: ", direcao == 1 ? "BUY" : "SELL");
Validação no Strategy Tester
Antes de ligar em conta real, sempre — sempre — backteste no Strategy Tester do MT5:
- Coloca o EA na pasta
MQL5\Expertse o.onnxemMQL5\Files - F7 no MetaEditor para compilar
- Ctrl+R no MT5 → Strategy Tester
- Configura ativo, timeframe e período (mínimo 6 meses)
- Modo “Every tick based on real ticks”
- Analisa profit factor, drawdown máximo, sharpe
✓ Checklist antes de operar real
1. Profit factor > 1.5 em pelo menos 6 meses backtest
2. Drawdown máximo < 20%
3. 200+ trades na amostra
4. Demo trading por 30 dias mínimo com resultado similar ao backtest
5. Risk por trade ≤ 2% do balance
6. Stop loss sempre definido
Troubleshooting comum
- “Operator not supported” ao exportar: ajuste
opset_version(tente 13, 14, 15, 17) - OnnxRun retorna false: verifique shapes — input do MQL5 precisa bater exatamente com o que foi declarado no export
- Predições nan ou constantes: feature scaling. Normalize as features no Python e replique a mesma normalização no MQL5
- Modelo gigante: simplifique. LSTM com hidden 32 é mais que suficiente para baseline
🚀 Teste seu modelo ONNX em demo Deriv MT5 ($10.000 virtuais):
Abrir Demo Deriv MT5Próximos passos
- Voltar ao pillar — ONNX no MT5 completo
- Referência das funções OnnxCreate / OnnxRun
- 5 modelos ONNX práticos (LSTM, XGBoost, CNN, RF, Transformer)
- ONNX vs Socket vs DLL — comparativo
