Desenvolvimento de ferramentas para análise de Price Action (Parte 36): Acesso direto do Python aos fluxos de dados de mercado do MetaTrader 5
Sumário
- Introdução
- Visão geral da arquitetura do sistema
- Análise detalhada do backend em Python
- Arquitetura do EA cliente em MQL5
- Avaliação e métricas de desempenho
- Conclusão
Introdução
No artigo anterior, mostramos como um simples script MQL5 podia enviar barras históricas ao Python, gerar atributos, treinar um modelo de machine learning e devolver sinais ao MetaTrader para execução, eliminando a necessidade de exportar arquivos CSV manualmente, realizar análises no Excel e lidar com problemas de controle de versão. Com isso, os traders passaram a contar com um pipeline completo, capaz de transformar dados brutos de barras de um minuto em pontos de entrada fundamentados estatisticamente, com níveis de stop-loss (SL) e take-profit (TP) calculados dinamicamente.
Esse sistema resolveu três problemas fundamentais da negociação algorítmica:
- Fragmentação dos dados. Não é mais necessário copiar e colar arquivos CSV nem lidar com fórmulas complexas em planilhas, pois o gráfico do MetaTrader 5 se comunica diretamente com o Python.
- Análises defasadas. A automação da engenharia de atributos e da inferência do modelo permitiu substituir uma abordagem reativa por uma negociação proativa, baseada em dados atualizados e sinais gerados em tempo real.
- Inconsistência na gestão de riscos. A incorporação de níveis de SL/TP baseados no ATR, tanto nos testes com dados históricos quanto na negociação em tempo real, garantiu que todas as operações seguissem regras ajustadas à volatilidade, preservando sua vantagem.

Entretanto, o envio de dados ao Python por meio de um EA pode introduzir latência e aumentar a complexidade do sistema. Na nova versão, o próprio Python atua como cliente do MetaTrader 5, utilizando a biblioteca MetaTrader 5 para acessar e atualizar os dados diretamente. Essa abordagem elimina a necessidade de aguardar o acionamento do temporizador do EA: o Python pode buscar dados sob demanda, armazená-los de forma eficiente no formato Parquet e executar tarefas de alto custo computacional de maneira assíncrona.
Com base nessa estrutura, nossa ferramenta aprimorada, uma solução híbrida que combina Python e MQL5, oferece recursos ainda mais avançados:
- No Python, a biblioteca nativa MetaTrader 5 permite obter dados de mercado em tempo real, enquanto a engenharia avançada de atributos gera variáveis como escores z associados a picos, variações do histograma do MACD, bandas baseadas no ATR e variações da tendência estimada pelo Prophet. O sistema também conta com um pipeline de gradient boosting que respeita a estrutura temporal dos dados e realiza o retreinamento do modelo em janelas deslizantes. Todos esses recursos ficam disponíveis por meio de uma API Flask leve.
- No MQL5, um EA robusto realiza consultas periódicas via REST, com lógica de novas tentativas em caso de falha. Ele também oferece um painel no gráfico com sinais, níveis de confiança e status da conexão, setas de entrada e saída, além da opção de executar ordens automaticamente, seguindo regras rigorosas de gestão de riscos.
Se o primeiro artigo apresentou apenas uma prova de conceito, este framework, pronto para uso em ambiente de produção, reduz significativamente o tempo de configuração, acelera o ciclo de feedback e permite negociar com mais confiança e precisão, tomando decisões fundamentadas em dados. Vamos começar.
Visão geral da arquitetura do sistema
A seguir, apresentamos uma visão geral de como os dados e sinais circulam entre o MetaTrader 5 e o serviço Python, além das principais responsabilidades de cada componente:

Terminal MetaTrader 5
O terminal MetaTrader 5 funciona como a principal interface de acesso ao mercado e plataforma de gráficos. Ele armazena barras de preços atuais e históricas do símbolo selecionado e fornece o ambiente de execução do EA. Por meio da função nativa WebRequest(), o EA se comunica periodicamente com o serviço Python, enquanto obtém as barras mais recentes diretamente do terminal e exibe no gráfico os sinais recebidos, as linhas de SL/TP e as setas de entrada e saída. O terminal MetaTrader 5 é responsável pelo envio de ordens (quando essa funcionalidade está habilitada), pelo gerenciamento dos objetos locais (painéis, setas e rótulos) e pela visualização dos resultados do sistema na interface do usuário.
Fluxo de dados no Python
Em vez de depender do EA para enviar os dados das barras, o componente Python Data Feed utiliza a biblioteca oficial MetaTrader 5 para Python, que permite obter, sob demanda, dados OHLC históricos e atuais de barras de um minuto. Inicialmente, o componente cria um repositório Parquet compactado para armazenar os dados dos dias anteriores. Em seguida, acrescenta novas barras à medida que ficam disponíveis. Essa arquitetura elimina a dependência dos intervalos do temporizador do MQL5 e garante que o serviço Python tenha acesso imediato e aleatório a todo o histórico de preços, tanto para testes com dados históricos quanto para inferência em tempo real.
Engenharia de atributos
Assim que as barras brutas ficam disponíveis na memória ou no disco, a camada de engenharia de atributos as transforma em variáveis de entrada estatisticamente relevantes para o modelo de machine learning. Ela calcula escores z normalizados associados a picos, diferenças entre valores do histograma MACD, valores do RSI de 14 períodos, o ATR de 14 períodos para medir a volatilidade e bandas dinâmicas do indicador Envelopes baseadas na EMA. Além disso, utiliza a biblioteca Prophet para estimar a variação da tendência em intervalos de um minuto, permitindo identificar a tendência do mercado de retornar à média ou manter o movimento predominante. Esse pipeline automatizado garante que os dados atuais e históricos sejam processados da mesma forma, mantendo a consistência dos atributos utilizados pelo modelo.
Modelo de machine learning
O núcleo do sistema é um classificador de gradient boosting integrado a um pipeline scikit-learn com padronização dos atributos. O modelo é treinado com janelas deslizantes de barras históricas, utilizando TimeSeriesSplit para evitar o viés causado pelo uso de informações futuras (conhecido como look-ahead bias) e RandomizedSearchCV para otimizar os hiperparâmetros. Os rótulos são definidos com base na variação do preço nos dez minutos seguintes, classificando os movimentos como BUY, SELL ou WAIT de acordo com um limiar configurável. O modelo treinado é serializado no arquivo model.pkl, permitindo carregamento e inferência com baixa latência, tanto nos testes com dados históricos quanto na operação em tempo real.
Flask API
A Flask API faz a conexão entre o ecossistema de análise de dados do Python e o EA em MQL5. Ela disponibiliza um único endpoint /analyze, que recebe um payload JSON contendo os preços de fechamento mais recentes e as respectivas marcas de tempo, aplica o pipeline de engenharia de atributos e o modelo carregado para calcular as probabilidades de cada classe e retorna uma resposta JSON compacta com signal, sl, tp e conf (nível de confiança). Essa interface REST leve pode ser executada em um contêiner ou implantada em qualquer servidor, separando os recursos computacionais do Python do ambiente de execução do MetaTrader e facilitando a escalabilidade.
EA MQL5
Do lado do cliente, o EA MQL5 é responsável exclusivamente pela interação com o usuário e pela execução das operações. Ele consulta periodicamente a Flask API, faz o parsing das respostas JSON recebidas, registra todos os sinais tanto na aba "Experts" quanto em um arquivo CSV local e atualiza o painel do gráfico com o sinal atual, o nível de confiança, o status da conexão e a marca de tempo. Ao receber um sinal válido de compra, venda ou fechamento, o EA desenha as setas e as linhas de SL/TP e, se EnableTrading estiver definido como true, envia ordens ou encerra posições por meio da classe CTrade. Ao concentrar toda a análise no Python, o EA permanece leve, responsivo e fácil de manter.
Análise detalhada do backend em Python
Nosso backend utiliza um pipeline robusto de carregamento de dados baseado no pacote oficial MetaTrader 5 para Python. Na primeira execução, o serviço realiza uma carga inicial, obtendo dados OHLC de barras de um minuto referentes aos últimos dias e armazenando-os em um arquivo Parquet compactado. O formato colunar do Parquet, combinado com a compressão Zstandard, permite ler rapidamente segmentos específicos das séries temporais, mantendo o consumo de espaço em disco reduzido. Depois disso, uma rotina simples de atualização incremental acrescenta apenas as novas barras, evitando operações desnecessárias de obtenção de dados e garantindo que tanto a inferência em tempo real quanto os testes com dados históricos utilizem uma fonte única e atualizada de dados.
import datetime as dt import pandas as pd import MetaTrader5 as mt5 from pathlib import Path PARQUET_FILE = "hist.parquet.zst" DAYS_TO_PULL = 60 UTC = dt.timezone.utc def bootstrap(): """Fetch last DAYS_TO_PULL days of M1 bars and write to Parquet.""" now = dt.datetime.utcnow() start = now - dt.timedelta(days=DAYS_TO_PULL) mt5.initialize() mt5.symbol_select("Boom 300 Index", True) bars = mt5.copy_rates_range("Boom 300 Index", mt5.TIMEFRAME_M1, start.replace(tzinfo=UTC), now.replace(tzinfo=UTC)) df = pd.DataFrame(bars) df['time'] = pd.to_datetime(df['time'], unit='s') df.set_index('time').to_parquet(PARQUET_FILE, compression='zstd') def append_new_bars(): """Append only the newest bars since last timestamp.""" df = pd.read_parquet(PARQUET_FILE) last = df.index[-1] now = dt.datetime.utcnow() new = mt5.copy_rates_range("Boom 300 Index", mt5.TIMEFRAME_M1, last.replace(tzinfo=UTC) + dt.timedelta(minutes=1), now.replace(tzinfo=UTC)) if new: new_df = pd.DataFrame(new) new_df['time'] = pd.to_datetime(new_df['time'], unit='s') merged = pd.concat([df, new_df.set_index('time')]) merged[~merged.index.duplicated()].to_parquet(PARQUET_FILE, compression='zstd')
Com as barras brutas já carregadas, nosso pipeline calcula um conjunto de atributos para avaliar o momentum, a volatilidade e os movimentos extremos do mercado. Transformamos a primeira diferença do preço em um indicador "z-spike", dividindo-a pelo desvio-padrão móvel de 20 barras para identificar picos abruptos de preço. A variação do histograma do MACD e o RSI de 14 períodos permitem avaliar, respectivamente, a tendência e as condições de sobrecompra e sobrevenda, enquanto o ATR de 14 períodos mede a volatilidade atual. A EMA de 20 períodos define as bandas do indicador Envelopes (EMA×0.997 e EMA×1.003), que se ajustam às mudanças nos regimes de mercado. Por fim, a biblioteca Prophet processa toda a série de preços de fechamento para prever a variação da tendência em intervalos de um minuto, permitindo considerar padrões mais sutis de sazonalidade e deriva que variam ao longo do tempo.
import numpy as np import pandas as pd import ta from prophet import Prophet def engineer(df: pd.DataFrame) -> pd.DataFrame: df = df.copy() # z-spike (20-bar rolling std) df['r'] = df['close'].diff() df['z_spike'] = df['r'] / (df['r'].rolling(20).std() + 1e-9) # MACD histogram diff, RSI, ATR df['macd'] = ta.trend.macd_diff(df['close']) df['rsi'] = ta.momentum.rsi(df['close'], window=14) df['atr'] = ta.volatility.average_true_range(df['high'], df['low'], df['close'], window=14) # EMA envelopes ema = df['close'].ewm(span=20).mean() df['env_low'] = ema * 0.997 df['env_up'] = ema * 1.003 # Prophet trend delta (minute-level) if len(df) > 200: m = Prophet(daily_seasonality=False, weekly_seasonality=False) m.fit(pd.DataFrame({'ds': df.index, 'y': df['close']})) df['delta'] = m.predict(m.make_future_dataframe(periods=0, freq='min'))['yhat'] - df['close'] else: df['delta'] = 0.0 return df.dropna()
Formulamos o problema de previsão como uma classificação em três classes: "BUY", quando o preço sobe mais do que um limiar definido nos dez minutos seguintes; "SELL", quando cai mais do que esse mesmo limiar; e "WAIT", nos demais casos. Após a atribuição dos rótulos, os atributos e os respectivos rótulos são divididos em janelas temporais deslizantes para o treinamento. O pipeline scikit-learn primeiro padroniza cada atributo e depois treina um classificador GradientBoostingClassifier. Os hiperparâmetros (taxa de aprendizado, número de árvores e profundidade máxima) são otimizados com RandomizedSearchCV, utilizando o esquema de validação cruzada TimeSeriesSplit para evitar o uso indevido de informações futuras (look-ahead bias). O melhor modelo é serializado no arquivo model.pkl e fica imediatamente disponível para inferência com baixa latência.
import numpy as np import joblib from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.ensemble import GradientBoostingClassifier from sklearn.model_selection import TimeSeriesSplit, RandomizedSearchCV LOOKAHEAD_MIN = 10 LABEL_THRESHOLD = 0.0015 FEATS = ['z_spike','macd','rsi','atr','env_low','env_up','delta'] def label_and_train(df: pd.DataFrame): # Look-ahead return chg = (df['close'].shift(-LOOKAHEAD_MIN) - df['close']) / df['close'] df['label'] = np.where(chg > LABEL_THRESHOLD, 1, np.where(chg < -LABEL_THRESHOLD, 2, 0)) X = df[FEATS].dropna() y = df.loc[X.index, 'label'] pipe = Pipeline([ ('scaler', StandardScaler()), ('gb', GradientBoostingClassifier(random_state=42)) ]) param_dist = { 'gb__learning_rate': [0.01, 0.05, 0.1], 'gb__n_estimators': [300, 500, 700], 'gb__max_depth': [2, 3, 4] } cv = TimeSeriesSplit(n_splits=5) rs = RandomizedSearchCV(pipe, param_dist, n_iter=12, cv=cv, scoring='roc_auc_ovr', n_jobs=-1, random_state=42) rs.fit(X, y) joblib.dump(rs.best_estimator_, 'model.pkl')Para conectar o Python ao MetaTrader, disponibilizamos um único endpoint Flask, /analyze. Os clientes enviam um payload JSON contendo o símbolo, um array de preços de fechamento e as respectivas marcas de tempo UNIX. O endpoint processa novamente esses dados pelo pipeline de engenharia de atributos, carrega o modelo previamente treinado, calcula as probabilidades de cada classe, seleciona o sinal com o maior nível de confiança e calcula dinamicamente os níveis de stop-loss e take-profit com base no atributo ATR. A resposta é um objeto JSON compacto:
from flask import Flask, request, jsonify import joblib import pandas as pd app = Flask(__name__) model = joblib.load('model.pkl') @app.route('/analyze', methods=['POST']) def analyze(): payload = request.get_json(force=True) closes = payload['prices'] times = pd.to_datetime(payload['timestamps'], unit='s') df = pd.DataFrame({'close': closes}, index=times) # duplicate open/high/low for completeness df[['open','high','low']] = df[['close']] feat = engineer(df).iloc[-1:] probs = model.predict_proba(feat[FEATS])[0] p_buy, p_sell = probs[1], probs[2] signal = ('BUY' if p_buy > 0.55 else 'SELL' if p_sell > 0.55 else 'WAIT') atr = feat['atr'] entry = feat['close'] sl = entry - atr if signal=='BUY' else entry + atr tp = entry + 2*atr if signal=='BUY' else entry - 2*atr return jsonify(signal=signal, sl=round(sl,5), tp=round(tp,5), conf=round(max(p_buy,p_sell),2)) if __name__ == '__main__': app.run(port=5000)
Arquitetura do EA cliente em MQL5
O laço principal do EA é executado por eventos do temporizador (OnTimer) ou pela detecção de uma nova barra, utilizando WebRequest para enviar e receber mensagens HTTP. Primeiro, o EA obtém as N barras mais recentes por meio de CopyRates, converte o array MqlRates em um payload JSON codificado em UTF-8, contendo o símbolo e a sequência de preços de fechamento, e configura os cabeçalhos HTTP necessários. Se WebRequest() falhar (retornando um valor ≤0), o EA consulta GetLastError(), incrementa o contador de tentativas, registra o erro no log e adia as próximas requisições até esgotar as tentativas ou ocorrer o próximo acionamento do temporizador. As respostas bem-sucedidas (código de status ≥200) reiniciam o contador de tentativas e atualizam lastStatus. Essa abordagem permite o processamento assíncrono confiável dos sinais, sem bloquear a thread do gráfico nem comprometer o funcionamento do sistema devido a problemas temporários de rede.
// In OnTimer() or OnNewBar(): MqlRates rates[]; // Copy the last N bars into `rates` if(CopyRates(_Symbol, _Period, 0, InpBufferBars, rates) != InpBufferBars) return; ArraySetAsSeries(rates, true); // Build payload string payload = "{"; payload += StringFormat("\"symbol\":\"%s\",", _Symbol); payload += "\"prices\":["; for(int i=0; i<InpBufferBars; i++) { payload += DoubleToString(rates[i].close, _digits); if(i < InpBufferBars-1) payload += ","; } payload += "]}"; // Send request string headers = "Content-Type: application/json\r\nAccept: application/json\r\n\r\n"; char req[], resp[]; int len = StringToCharArray(payload, req, 0, WHOLE_ARRAY, CP_UTF8); ArrayResize(req, len); ArrayResize(resp, 8192); int status = WebRequest("POST", InpServerURL, headers, "", InpTimeoutMs, req, len, resp, headers); if(status <= 0) { int err = GetLastError(); PrintFormat("WebRequest error %d (attempt %d/%d)", err, retryCount+1, MaxRetry); ResetLastError(); retryCount = (retryCount+1) % MaxRetry; lastStatus = StringFormat("Err%d", err); return; } retryCount = 0; lastStatus = StringFormat("HTTP %d", status);
Após o parsing de uma resposta JSON válida e a extração dos campos signal, sl e tp, o EA atualiza o painel do gráfico e desenha novas setas e linhas. O painel consiste em um objeto OBJ_RECTANGLE_LABEL com quatro rótulos de texto que exibem o símbolo, o sinal atual, o código de status HTTP e a marca de tempo. Para representar as operações, o EA primeiro remove todos os objetos existentes com o prefixo correspondente e depois cria uma nova seta (OBJ_ARROW) no nível do preço atual, utilizando códigos e cores diferentes para compra (seta verde para cima), venda (seta vermelha para baixo) e fechamento (seta laranja). As linhas horizontais (OBJ_HLINE) indicam os níveis de stop-loss e take-profit, representados em vermelho e verde, respectivamente. Como cada objeto utiliza um prefixo exclusivo do respectivo gráfico e os objetos são removidos quando o sinal muda ou durante a desinicialização, o gráfico permanece limpo e sem excesso de elementos visuais.
// Panel (rectangle + labels) void DrawPanel() { const string pid = "SigPanel"; if(ObjectFind(0, pid) < 0) ObjectCreate(0, pid, OBJ_RECTANGLE_LABEL, 0, 0, 0); ObjectSetInteger(0, pid, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, pid, OBJPROP_XDISTANCE, PanelX); ObjectSetInteger(0, pid, OBJPROP_YDISTANCE, PanelY); ObjectSetInteger(0, pid, OBJPROP_XSIZE, PanelW); ObjectSetInteger(0, pid, OBJPROP_YSIZE, PanelH); ObjectSetInteger(0, pid, OBJPROP_BACK, true); ObjectSetInteger(0, pid, OBJPROP_BGCOLOR, PanelBG); ObjectSetInteger(0, pid, OBJPROP_COLOR, PanelBorder); string lines[4] = { StringFormat("Symbol : %s", _Symbol), StringFormat("Signal : %s", lastSignal), StringFormat("Status : %s", lastStatus), StringFormat("Time : %s", TimeToString(TimeLocal(), TIME_MINUTES)) }; for(int i=0; i<4; i++) { string lbl = pid + "_L" + IntegerToString(i); if(ObjectFind(0, lbl) < 0) ObjectCreate(0, lbl, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, lbl, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, lbl, OBJPROP_XDISTANCE, PanelX + 6); ObjectSetInteger(0, lbl, OBJPROP_YDISTANCE, PanelY + 4 + i*(TxtSize+2)); ObjectSetString(0, lbl, OBJPROP_TEXT, lines[i]); ObjectSetInteger(0, lbl, OBJPROP_FONTSIZE, TxtSize); ObjectSetInteger(0, lbl, OBJPROP_COLOR, TxtColor); } } // Arrows & SL/TP lines void ActOnSignal(ESignal code, double sl, double tp) { // remove old arrows/lines for(int i=ObjectsTotal(0)-1; i>=0; i--) if(StringFind(ObjectName(0,i), objPrefix) == 0) ObjectDelete(0, ObjectName(0,i)); // arrow int arrCode = (code==SIG_BUY ? 233 : code==SIG_SELL ? 234 : 158); color clr = (code==SIG_BUY ? clrLime : code==SIG_SELL ? clrRed : clrOrange); string name = objPrefix + "Arr_" + TimeToString(TimeCurrent(), TIME_SECONDS); ObjectCreate(0, name, OBJ_ARROW, 0, TimeCurrent(), SymbolInfoDouble(_Symbol, SYMBOL_BID)); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrCode); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); // SL line if(sl > 0) { string sln = objPrefix + "SL_" + name; ObjectCreate(0, sln, OBJ_HLINE, 0, 0, sl); ObjectSetInteger(0, sln, OBJPROP_COLOR, clrRed); } // TP line if(tp > 0) { string tpn = objPrefix + "TP_" + name; ObjectCreate(0, tpn, OBJ_HLINE, 0, 0, tp); ObjectSetInteger(0, tpn, OBJPROP_COLOR, clrLime); } }
O envio efetivo de ordens é controlado pelo sinalizador EnableTrading, permitindo alternar facilmente entre o modo de visualização e o modo de execução real. Antes de enviar qualquer ordem a mercado, o EA utiliza PositionSelect(_Symbol) para verificar se já existe uma posição aberta e evitar duplicações. Para um sinal BUY, chama CTrade.Buy() com os argumentos FixedLots, SL e TP; para SELL, utiliza CTrade.Sell(); e, para CLOSE, chama CTrade.PositionClose(). O deslizamento de preço permitido (SlippagePoints) é considerado no fechamento da posição. Essa lógica simples, que mantém o estado das operações, impede a abertura de posições duplicadas na mesma direção e garante que todas as ordens respeitem os parâmetros de risco predefinidos.
void ExecuteTrade(ESignal code, double sl, double tp) { if(!EnableTrading) return; bool hasPosition = PositionSelect(_Symbol); if(code == SIG_BUY && !hasPosition) trade.Buy(FixedLots, _Symbol, 0, sl, tp); else if(code == SIG_SELL && !hasPosition) trade.Sell(FixedLots, _Symbol, 0, sl, tp); else if(code == SIG_CLOSE && hasPosition) trade.PositionClose(_Symbol, SlippagePoints); }
Instalação e configuração
Antes de começar a receber sinais em tempo real, é necessário preparar tanto o ambiente Python quanto a plataforma MetaTrader 5. Comece instalando o Python 3.8 ou uma versão mais recente no mesmo computador em que o terminal MetaTrader 5 está em execução. Crie e ative um ambiente virtual (execute python -m venv venv e, em seguida, venv\Scripts\activate no Windows ou source venv/bin/activate no macOS/Linux). Depois, instale todas as dependências com o comando:No MetaTrader 5, acesse:
Por exemplo, insira http://127.0.0.1:5000 no campo "Permitir WebRequest para as seguintes URLs". Essa inclusão na lista de URLs permitidas é essencial, pois, sem ela, o MetaTrader 5 descartará todas as requisições POST sem apresentar uma notificação explícita.
Na pasta do projeto, copie o script do serviço Python (por exemplo, market_ai_engine.py) para o diretório de trabalho escolhido. No início do script, configure o símbolo de negociação (MAIN_SYMBOL), as credenciais da conta MetaTrader 5 (LOGIN_ID, PASSWORD, SERVER) e os caminhos dos arquivos (PARQUET_FILE, MODEL_FILE). Se precisar utilizar uma porta diferente da padrão para o servidor Flask, você poderá especificá-la pelo parâmetro --port ao iniciar o serviço.
Para instalar o EA, coloque o arquivo compilado EA.ex5 (ou o arquivo-fonte .mq5) no diretório MQL5 → Experts da instalação do MetaTrader 5.
Reinicie o MetaTrader 5 ou atualize o "Navegador" para que o EA apareça na lista. Arraste-o para o gráfico M1 do mesmo símbolo configurado no Python. Nos parâmetros de entrada do EA, defina InpServerURL como http://127.0.0.1:5000/analyze e configure InpBufferBars (por exemplo, 60), InpPollInterval (por exemplo, 60 segundos) e InpTimeoutMs (por exemplo, 5000 ms). Inicialmente, mantenha EnableTrading desativado para verificar os sinais sem executar ordens reais.
Por fim, inicie o backend Python executando os seguintes comandos, nesta ordem:
python market_ai_engine.py bootstrap
python market_ai_engine.py collect
python market_ai_engine.py train
python market_ai_engine.py serve --port 5000
Com o servidor Flask em execução e, no MetaTrader, o AutoTrading habilitado, o EA começa a consultar os sinais em tempo real e a desenhar as setas e linhas de SL/TP no gráfico. Quando você estiver pronto, poderá habilitar a execução de operações conforme as regras de risco predefinidas.
Solução de problemas
Se o EA não exibir dados ou retornar sempre "WAIT", verifique se a URL da API foi adicionada à lista de URLs permitidas nas configurações dos Expert Advisors do MetaTrader 5. Em ambientes que combinam HTTP e HTTPS, utilize HTTP convencional (127.0.0.1) para testes locais e HTTPS com um certificado confiável em produção. Verifique também se os relógios do servidor e do terminal MetaTrader 5 estão sincronizados (ambos em UTC ou no mesmo fuso horário), para evitar deslocamentos temporais nas consultas de barras. Por fim, confirme que o AutoTrading está habilitado e que nenhuma permissão global ou outro EA está impedindo o funcionamento do seu EA.
Avaliação e métricas de desempenho
Para começar a utilizar o sistema híbrido de machine learning Python-MQL5, primeiro é necessário realizar a carga inicial dos dados históricos com o comando:
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py bootstrap 2025-08-04 23:39:23 | INFO | Bootstrapped historical data: 86394 rows
Após a carga inicial, é possível manter os dados continuamente atualizados com o comando:
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py collect 2025-08-04 23:41:01 | INFO | Appended 2 new bars 2025-08-04 23:42:01 | INFO | Appended 1 new bars 2025-08-04 23:43:01 | INFO | Appended 1 new bars
Com o conjunto de dados atualizado, a próxima etapa é treinar o modelo.
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py train 23:48:44 - cmdstanpy - INFO - Chain [1] start processing 23:51:24 - cmdstanpy - INFO - Chain [1] done processing 2025-08-05 02:59:08 | INFO | Model training complete
Para avaliar o desempenho potencial do modelo em condições reais de negociação, você pode executar um teste com dados históricos utilizando o comando:
C:\Users\hp\Desktop\Intrusion Trader>python market_ai_engine.py backtest --days 30 06:57:33 - cmdstanpy - INFO - Chain [1] start processing 06:58:30 - cmdstanpy - INFO - Chain [1] done processing 2025-08-05 06:59:20 | INFO | Backtest results saved to backtest_results_30d.csv
Resultados dos testes com dados históricos
A seguir, apresentamos algumas métricas e informações sobre as operações obtidas no teste com dados históricos de 30 dias:

Gráfico da evolução do capital total ao longo do tempo

Veja um resumo das principais métricas:
* **Average Entry Price:** 3099.85 * **Average Exit Price:** 3096.53 * **Average PNL (Profit and Loss):** 3.32 * **Total PNL:** 195.69 * **Average Cumulative Equity:** 96.34 * **First Trade Time:** 2025-07-11 14:18:00 * **Last Trade Time:** 2025-07-27 02:00:00
Percentual de operações vencedoras:
win_rate 72.88135528564453 The win rate is 72.88%. This means that approximately 73% of the trades resulted in a profit.
Depois que o modelo estiver treinado e validado, você poderá iniciar o servidor de inferência em tempo real com o comando:
2025-08-05 12:41:53 | INFO | analyze: signal=%s, sl=%.5f, tp=%.5f 127.0.0.1 - - [05/Aug/2025 12:41:53] "POST /analyze HTTP/1.1" 200 -
No MetaTrader, o EA precisa estar configurado corretamente para consultar o servidor Python. Nos parâmetros de entrada, defina a URL do servidor (por exemplo, http://127.0.0.1:5000/analyze) e vincule o EA a um gráfico do mesmo símbolo e timeframe utilizados no treinamento do modelo, geralmente M1. Após a inicialização, o EA consultará periodicamente os sinais, exibirá setas no gráfico e, se habilitado, executará operações seguindo regras rigorosas de gestão de riscos.
2025.08.05 12:41:53.532 trained model (1) (Boom 300 Index,M1) >>> JSON: {"symbol":"Boom 300 Index","prices":[2701.855,2703.124, 2704.408,2705.493,2705.963,2696.806,2698.278,2699.877,2701.464,2702.788,2691.762,2693.046,2694.263,2695.587,2696.863,2698. 179,2699.775,2701.328,2702.888,2698.471,2699.887,2695.534,2696.952,2698.426,2699.756,2699.552,2700.954,2702.131,2703.571, 2699.549,2700.868,2702.567,2703.798,2705.067,2706.874,2698.084,2699.538,2700.856,2702.227,2703.692,2705.102,2706.188,2707.609,2709.001, 2710.335,2711.716,2712.919,2712.028,2713.529,2715.052,2716.578,2717. 2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1) <<< HTTP 200 hdr: 2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1) {"conf":0.43,"signal":"WAIT","sl":2725.04317,"tp":2720.18266} 2025.08.05 12:41:53.943 trained model (1) (Boom 300 Index,M1) [2025.08.05 12:41:53] Signal → WAIT | SL=2725.04317 | TP=2720.18266 | Conf=0.43
Conclusão
Neste artigo, desenvolvemos um EA MQL5 que aproveita os recursos avançados de análise de dados do Python. Agora, você conta com os seguintes componentes:- Fluxo de dados: o componente Python obtém barras de um minuto diretamente do MetaTrader 5 e as armazena no formato Parquet, enquanto o EA consulta os sinais e os exibe no gráfico.
- Engenharia de atributos: o sistema calcula spike-z, MACD, RSI, ATR, bandas do indicador Envelopes e até variações da tendência estimadas pelo Prophet.
- Modelagem e disponibilização de sinais: você treina um modelo de gradient boosting que respeita a estrutura temporal dos dados e disponibiliza as previsões por meio do Flask.
- Operação no gráfico: o EA MQL5 utiliza esses sinais para desenhar setas e linhas de SL/TP, além de permitir a abertura e o fechamento automático de operações.
Você pode experimentar outros algoritmos (XGBoost, LSTM etc.), refinar as regras de gestão de riscos ou empacotar o serviço Python em um contêiner Docker para simplificar a implantação. Com essa base, você poderá aprimorar os resultados dos testes com dados históricos e continuar evoluindo suas estratégias automatizadas.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/19065
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.