English Русский 中文 Español Deutsch 日本語
preview
Desenvolvimento de ferramentas para análise de Price Action (Parte 36): Acesso direto do Python aos fluxos de dados de mercado do MetaTrader 5

Desenvolvimento de ferramentas para análise de Price Action (Parte 36): Acesso direto do Python aos fluxos de dados de mercado do MetaTrader 5

MetaTrader 5 — Sistemas de negociação |
21 0
Christian Benjamin
Christian Benjamin

Sumário


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:

  1. 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.
  2. 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.
  3. 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:
pip install MetaTrader 5 pandas numpy ta prophet scikit-learn flask loguru joblib
Em seguida, configure o Firewall do Windows para permitir requisições HTTP de saída dos executáveis python.exe e terminal64.exe. Se pretende implantar o sistema em um ambiente de produção com HTTPS, instale o certificado SSL no repositório de autoridades certificadoras raiz confiáveis, para que o MetaTrader 5 aceite conexões seguras.

No MetaTrader 5, acesse:

Ferramentas → Opções → Expert Advisors, habilite a importação de DLLs e adicione o endereço local da sua API.

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:

python market_ai_engine.py bootstrap
Esse comando inicializa o conjunto de dados local, obtendo diretamente do MetaTrader 5 as barras M1 (de um minuto) dos últimos 60 dias, por meio da biblioteca nativa MetaTrader 5 para Python. Os dados são armazenados em um arquivo Parquet compactado (hist.parquet.zst), que permite acesso rápido aos dados em disco e utiliza o espaço de armazenamento de forma eficiente. Essa etapa precisa ser executada apenas uma vez, a menos que você queira reinicializar completamente o histórico de dados.
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:

python market_ai_engine.py collect
Isso mantém o conjunto de dados atualizado em tempo real. Embora esse coletor possa ser executado em segundo plano, ele só é necessário se você quiser manter o fluxo de dados continuamente atualizado, sem precisar repetir a carga inicial antes de cada treinamento.
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.

python market_ai_engine.py train
Esse comando executa o pipeline completo de machine learning. O script lê o arquivo Parquet, aplica técnicas de engenharia de atributos (como detecção de picos, variações do histograma do MACD, RSI, bandas baseadas no ATR, bandas do indicador Envelopes e tendências estimadas pelo Prophet) e utiliza GradientBoostingClassifier em conjunto com RandomizedSearchCV para treinar um modelo preditivo em uma janela deslizante de dados recentes. O resultado é um modelo serializado, salvo no arquivo model.pkl e pronto para inferência.
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:

python market_ai_engine.py backtest --days 30
Esse comando simula a geração de sinais e os resultados das operações nos últimos 30 dias, calculando métricas como a taxa de acerto dos sinais, a proporção entre operações lucrativas e operações com prejuízo e a rentabilidade total. Os resultados são salvos em um arquivo CSV para facilitar a análise e ajudam a avaliar se a estratégia do sistema atende às suas expectativas.
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:

python market_ai_engine.py serve --port 5000
Esse comando inicia uma API Flask leve que recebe as requisições enviadas pelo EA MQL5. Quando o EA consulta o endpoint /analyze, o servidor obtém imediatamente a barra mais recente, calcula os atributos correspondentes, executa o modelo e retorna uma previsão com os respectivos valores de SL, TP e nível de confiança em formato JSON. Esse servidor estabelece a conexão em tempo real entre o gráfico e o mecanismo de inteligência artificial.
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.
Ao longo do desenvolvimento, você também conheceu detalhes importantes da implementação, desde o parsing de JSON contendo dados adicionais no Flask até o limitação da frequência das requisições HTTP no EA, e viu como a separação de responsabilidades facilita a manutenção e a expansão do sistema.
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.






Chart Projector
Analytical Comment
Analytics Master
Analytics Forecaster
Volatility Navigator
Mean Reversion Signal Reaper
Signal Pulse
Metrics Board
External Flow
VWAP
Heikin Ashi FibVWAP
RSI DIVERGENCE
Parabolic Stop and Reverse (PSAR)
Script Quarters Drawer
Intrusion Detector
TrendLoom Tool Quarters Board
ZigZag Analyzer Correlation Pathfinder Market Structure Flip Detector Tool
Correlation Dashboard Currency Strength Meter
PAQ Analysis Tool
Dual EMA Fractal Breaker
Pin bar, Engulfing and RSI divergence
Liquidity Sweep Opening Range Breakout Tool Boom and Crash Interceptor CCI Zer-Line EA
Candlestick Recognition Candlestick Detection using TA-Lib Candle Range Tool MetaTrader 5 Data Ingestor Model Training and Deployment Use of Python Lib

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/19065

Arquivos anexados |
Automação de estratégias de trading em MQL5 (Parte 27): Identificação e Visualização do Padrão Harmônico "Crab" com Base em Price Action Automação de estratégias de trading em MQL5 (Parte 27): Identificação e Visualização do Padrão Harmônico "Crab" com Base em Price Action
Neste artigo, desenvolveremos um sistema de reconhecimento do padrão harmônico "Crab" em MQL5, capaz de identificar formações de alta e de baixa com base em pontos de reversão e níveis de Fibonacci. O sistema executará operações com níveis precisos de entrada, stop-loss e take-profit. Também adicionaremos recursos de visualização por meio de objetos gráficos, como triângulos e linhas de tendência, para representar a estrutura XABCD do padrão e os níveis de negociação.
Automação de estratégias de negociação em MQL5 (Parte 26): Criação de um sistema de preço médio baseado em pin bars para negociação com múltiplas posições Automação de estratégias de negociação em MQL5 (Parte 26): Criação de um sistema de preço médio baseado em pin bars para negociação com múltiplas posições
Neste artigo, desenvolvemos em MQL5 um sistema de preço médio baseado em pin bars, que identifica esses padrões para abrir operações e utiliza uma estratégia de preço médio para gerenciar múltiplas posições, com recursos de trailing stop e ajuste do stop para o ponto de equilíbrio (breakeven). Também combinamos parâmetros configuráveis com um dashboard que permite monitorar as posições e os lucros em tempo real.
Oscilador Parafrac: combinação dos indicadores Parabolic SAR e Fractals Oscilador Parafrac: combinação dos indicadores Parabolic SAR e Fractals
Neste artigo, veremos como combinar o Parabolic SAR e o indicador Fractals para criar um novo indicador do tipo oscilador. Ao aproveitar os pontos fortes de ambas as ferramentas, os traders podem desenvolver uma estratégia de negociação mais precisa e eficiente.
Automatização de estratégias de negociação em MQL5 (Parte 25): EA para negociação com linhas de tendência ajustadas pelo método dos mínimos quadrados e geração dinâmica de sinais Automatização de estratégias de negociação em MQL5 (Parte 25): EA para negociação com linhas de tendência ajustadas pelo método dos mínimos quadrados e geração dinâmica de sinais
Neste artigo, desenvolveremos um programa de negociação baseado em linhas de tendência que utiliza o ajuste pelo método dos mínimos quadrados (least squares fit) para determinar as linhas de tendência que atuam como suporte e resistência. O programa gera sinais dinâmicos de compra e venda quando o preço toca essas linhas e abre posições com base nos sinais identificados.