English Русский 中文 Español Deutsch 日本語
preview
Desenvolvimento de ferramentas para análise de Price Action (Parte 32): Módulo de reconhecimento de padrões de candles em Python (II): Reconhecimento com TA-Lib

Desenvolvimento de ferramentas para análise de Price Action (Parte 32): Módulo de reconhecimento de padrões de candles em Python (II): Reconhecimento com TA-Lib

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

Sumário



Introdução

No artigo anterior, mostrei como utilizar Python para reconhecer padrões de candles. Inicialmente, adotamos métodos manuais de reconhecimento, nos quais cada padrão era definido explicitamente no código. Nessa abordagem, o MQL5 envia ao Python os dados de preços, incluindo abertura, fechamento, máxima e mínima, além da marca de tempo. O Python então processa essas informações e identifica os padrões por meio de cálculos específicos.

Neste artigo, vamos aprimorar essa abordagem integrando a biblioteca TA-Lib ao Python, o que permitirá reconhecer automaticamente mais de 60 padrões de candles. Também utilizaremos a TA-Lib em conjunto com mplfinance e matplotlib para gerar gráficos de candles.

A seguir, apresentamos uma visão geral das principais bibliotecas utilizadas.

from flask import Flask, reque
st, jsonify, send_file
import numpy as np
import pandas as pd
import talib
import json
import matplotlib.pyplot as plt
import mplfinance as mpf

Vamos conhecer brevemente a função de cada uma delas.

Flask

from flask import Flask, request, jsonify, send_file

  • Framework web leve para Python, utilizado no desenvolvimento de aplicações web e APIs.
  • A classe Flask é utilizada para criar uma instância da aplicação.
request: permite acessar os dados das requisições recebidas, como as informações enviadas por meio dos métodos POST ou GET.

jsonify: converte estruturas de dados do Python para o formato JSON, permitindo retorná-las nas respostas.

send_file: permite enviar arquivos do servidor ao cliente, sendo útil, por exemplo, para o download de imagens ou relatórios gerados pela aplicação.

import numpy as np

  • NumPy: biblioteca fundamental para cálculos numéricos em Python.
  • np: alias convencional de NumPy.

A biblioteca permite realizar operações com arrays, cálculos matemáticos e transformações de dados de maneira eficiente.

import pandas as pd

  • Pandas: biblioteca avançada para processamento e transformação de dados.
  • pd: alias convencional de Pandas.

É utilizada para trabalhar com estruturas de dados como DataFrames, especialmente adequadas ao processamento de séries temporais, incluindo dados de candles.

import talib

  • TA-Lib: biblioteca de análise técnica para Python.

Oferece funções para calcular automaticamente uma ampla variedade de indicadores técnicos e reconhecer padrões de candles.

import json

  • Módulo da biblioteca padrão do Python para trabalhar com dados JSON.

Permite fazer o parsing dos dados JSON recebidos nas requisições ou converter objetos Python para JSON ao gerar respostas.

import matplotlib.pyplot as plt

  • Matplotlib: biblioteca para criação de gráficos.
  • pyplot: módulo do Matplotlib que oferece uma interface semelhante à do MATLAB.
  • plt: alias convencional de pyplot.
  • É utilizada para criar gráficos estáticos, animados e interativos, incluindo gráficos de candles.

import mplfinance as mpf

  • mplfinance: biblioteca especializada na visualização de dados financeiros, especialmente gráficos de candles e OHLC.
  • mpf: alias utilizado por conveniência.
  • Simplifica a criação de gráficos financeiros e oferece recursos avançados de personalização.

A seguir, apresentamos as vantagens de utilizar a TA-Lib para reconhecer padrões de candles:

VantagemDescrição
AutomaçãoReconhece automaticamente mais de 60 padrões de candles, sem a necessidade de implementar manualmente a lógica de cada um.
EficiênciaReconhecimento mais rápido de padrões, adequado à análise em tempo real.
PrecisãoUtiliza algoritmos amplamente testados para reconhecer padrões de forma confiável.
CompatibilidadeIntegra-se facilmente a outras bibliotecas Python de análise e visualização de dados.
Facilidade de usoSimplifica a implementação por meio de chamadas de função simples e claras.


Continuação da análise anterior

Como mencionei anteriormente, o sistema que desenvolvemos utilizava uma implementação manual em Python para reconhecer padrões de candles. Vamos recapitular brevemente como ele funcionava.

  • EA do MetaTrader 5 ("coordenador no gráfico")

No MetaTrader 5, o EA monitora o surgimento de cada nova barra e coleta imediatamente os preços de abertura, máxima, mínima e fechamento, além das marcas de tempo dos últimos 31 candles. Em seguida, reúne esses dados em um pacote JSON compacto e os envia, por meio de WebRequest, a um serviço local de reconhecimento de padrões. Quando o servidor retorna um array com os nomes dos padrões de candles identificados,

MQL5 EA ➔ (OHLC JSON POST) ➔ Python Server

o EA posiciona automaticamente cada rótulo na máxima da barra correspondente, utilizando o tamanho da fonte e a cor definidos pelo usuário, e aciona um alerta do MetaTrader 5 para cada padrão válido. A cada tick, os rótulos anteriores também são removidos para manter o gráfico limpo. Quando o EA é removido, ele exclui todos os objetos que criou, sem deixar nenhum elemento residual no gráfico.

MQL5 EA draws labels & fires alerts

  • Servidor Flask em Python: módulo de reconhecimento de padrões

No Python, criei uma aplicação Flask com o endpoint /patterns. Ela recebe um array JSON contendo dados OHLC, converte as marcas de tempo UNIX em datas e horários UTC e cria um DataFrame do pandas para permitir uma indexação confiável. Em seguida, aplico um conjunto de regras clássicas de análise de candles implementadas em Python puro, incluindo doji, martelo, engolfo, harami, estrela da manhã, estrela da noite e outros padrões. Cada um deles possui uma função específica, com lógica clara e passível de testes.

def detect_patterns(df):
    pats = ["None"]*len(df)
    for i in range(len(df)):
        o,h,l,c = df.iloc[i][["OPEN","HIGH","LOW","CLOSE"]]
        body = abs(c-o); rng = max(h-l,1e-6)
        lower = min(o,c)-l; upper = h-max(o,c)
        # doji
        if body/rng <= 0.1:
            pats[i] = "doji"; continue
        # hammer
        if lower>=2*body and upper<=body:
            pats[i] = "hammer"; continue
        # shooting star
        if upper>=2*body and lower<=body:
            pats[i] = "shootingstar"; continue
        # bullish engulfing
        if i>0:
            po,pc = df.iloc[i-1][["OPEN","CLOSE"]]
            if pc<po and c>o and o<=pc and c>=po:
                pats[i] = "bullishengulfing"; continue
        # bearish engulfing
        if i>0:
            po,pc = df.iloc[i-1][["OPEN","CLOSE"]]
            if pc>po and c<o and o>=pc and c<=po:
                pats[i] = "bearishengulfing"; continue
        # harami
        if i>0:
            po,pc = df.iloc[i-1][["OPEN","CLOSE"]]
            if pc<po and o<c and o>pc and c<po:
                pats[i] = "bullishharami"; continue
            if pc>po and o>c and o<pc and c>po:
                pats[i] = "bearishharami"; continue
        # morning star
        if i>1:
            o1,c1 = df.iloc[i-2][["OPEN","CLOSE"]]
            o2,c2 = df.iloc[i-1][["OPEN","CLOSE"]]
            if c1<o1 and abs(c2-o2)<(df.iloc[i-1]["HIGH"]-df.iloc[i-1]["LOW"])*0.3 \
               and c>o2 and c>(o1+c1)/2:
                pats[i] = "morningstar"; continue
        # evening star
        if i>1:
            o1,c1 = df.iloc[i-2][["OPEN","CLOSE"]]
            o2,c2 = df.iloc[i-1][["OPEN","CLOSE"]]
            if c1>o1 and abs(c2-o2)<(df.iloc[i-1]["HIGH"]-df.iloc[i-1]["LOW"])*0.3 \
               and c<o2 and c<(o1+c1)/2:
                pats[i] = "eveningstar"; continue
    return pats

Após rotular cada barra, conto todas as ocorrências diferentes de "None", registro um breve resumo no log e meço o tempo de processamento em milissegundos. Por fim, retorno um objeto JSON contendo a lista completa de nomes de padrões, com uma entrada correspondente a cada barra, além do log. Essa divisão mantém o processamento mais pesado dos dados no Python, onde o código é mais fácil de manter e ampliar, enquanto o MetaTrader 5 fica responsável pela entrada e saída dos dados do gráfico e pela visualização.

Python Server ➔ (patterns & log JSON) ➔ MQL5 EA


Conhecendo a TA-Lib

A TA-Lib (Technical Analysis Library) é uma biblioteca de código aberto amplamente utilizada por traders, investidores e analistas para realizar cálculos avançados de análise técnica e desenvolver estratégias de negociação. Desenvolvida originalmente por Mario Fortier, a biblioteca foi escrita em ANSI C e oferece um amplo conjunto de mais de 200 indicadores técnicos, incluindo ADX, MACD, RSI, Stochastic Oscillator, Bollinger Bands, entre outros, além de permitir o reconhecimento de mais de 60 padrões de candles. Seu núcleo em C/C++ disponibiliza uma API que também pode ser acessada pelo Python, facilitando a integração com diferentes aplicações. Desde seu lançamento inicial em 2001, sob a licença BSD, a TA-Lib consolidou-se como uma ferramenta estável e confiável. Seus algoritmos resistiram ao teste do tempo e continuam sendo amplamente utilizados tanto em projetos de código aberto quanto em aplicações comerciais.

Em nosso sistema, o amplo conjunto de funções de reconhecimento de padrões da TA-Lib foi integrado ao pipeline de análise em Python para automatizar a identificação de padrões de candles. O sistema carrega dinamicamente todas as funções necessárias ao reconhecimento de padrões de candles da TA-Lib, permitindo identificar mais de 60 padrões diferentes nos dados de mercado recebidos. Ao combinar os algoritmos confiáveis da TA-Lib com uma lógica de filtragem personalizada, o sistema identifica com precisão sinais de alta e de baixa, que são posteriormente representados nos gráficos de candles por meio do mplfinance. Todo esse fluxo é implementado como um serviço Flask, facilitando o processamento de dados em tempo real, o reconhecimento de padrões e a visualização dos resultados. Essa abordagem demonstra como os recursos da TA-Lib podem ser combinados com ferramentas modernas do Python para desenvolver sistemas avançados de análise automatizada de trading, que complementam naturalmente as estratégias desenvolvidas em MQL5.

A seguir, apresentamos um guia passo a passo para instalar a TA-Lib em seu computador.

Etapa 1: baixe e instale o arquivo .whl da TA-Lib

  • Para Windows:

Baixe os arquivos binários pré-compilados de uma fonte confiável. Escolha o arquivo .whl correspondente ao seu ambiente, por exemplo, TA_Lib‑0.4.0‑cp39‑cp39‑win_amd64.whl, compatível com Python 3.9 no Windows de 64 bits. Nesse contexto, o arquivo .whl deve corresponder tanto à versão do Python quanto à arquitetura do sistema para ser compatível.

Execute a instalação utilizando o pip:

pip install path\to\your\downloaded\file.whl

  • Para macOS:

Instale a biblioteca TA-Lib em C utilizando o Homebrew:

brew install ta-lib

  • Para Linux (Debian/Ubuntu):

Instale as dependências utilizando o apt:

sudo apt-get update
sudo apt-get install libta-lib0-dev

Etapa 2: instale o wrapper Python da TA-Lib

Após instalar a biblioteca em C, instale o wrapper para Python:

pip install ta-lib

Etapa 3: verifique a instalação

Abra o shell do Python e execute:

import talib
print(talib.__version__)

Se nenhum erro ocorrer e a versão da biblioteca for exibida na tela, a instalação foi concluída com sucesso.

Vamos conhecer os padrões de candles disponíveis na versão instalada da biblioteca TA-Lib.

CDL2CROWS
  CDL3BLACKCROWS
  CDL3INSIDE
  CDL3LINESTRIKE
  CDL3OUTSIDE
  CDL3STARSINSOUTH
  CDL3WHITESOLDIERS
  CDLABANDONEDBABY
  CDLADVANCEBLOCK
  CDLBELTHOLD
  CDLBREAKAWAY
  CDLCLOSINGMARUBOZU
  CDLCONCEALBABYSWALL
  CDLCOUNTERATTACK
  CDLDARKCLOUDCOVER
  CDLDOJI
  CDLDOJISTAR
  CDLDRAGONFLYDOJI
  CDLENGULFING
  CDLEVENINGDOJISTAR
  CDLEVENINGSTAR
  CDLGAPSIDESIDEWHITE
  CDLGRAVESTONEDOJI
  CDLHAMMER
  CDLHANGINGMAN
  CDLHARAMI
  CDLHARAMICROSS
  CDLHIGHWAVE
  CDLHIKKAKE
  CDLHIKKAKEMOD
  CDLHOMINGPIGEON
  CDLIDENTICAL3CROWS
  CDLINNECK
  CDLINVERTEDHAMMER
  CDLKICKING
  CDLKICKINGBYLENGTH
  CDLLADDERBOTTOM
  CDLLONGLEGGEDDOJI
  CDLLONGLINE
  CDLMARUBOZU
  CDLMATCHINGLOW
  CDLMATHOLD
  CDLMORNINGDOJISTAR
  CDLMORNINGSTAR
  CDLONNECK
  CDLPIERCING
  CDLRICKSHAWMAN
  CDLRISEFALL3METHODS
  CDLSEPARATINGLINES
  CDLSHOOTINGSTAR
  CDLSHORTLINE
  CDLSPINNINGTOP
  CDLSTALLEDPATTERN
  CDLSTICKSANDWICH
  CDLTAKURI
  CDLTASUKIGAP
  CDLTHRUSTING
  CDLTRISTAR
  CDLUNIQUE3RIVER
  CDLUPSIDEGAP2CROWS
  CDLXSIDEGAP3METHODS

Embora a maioria dos traders conheça bem padrões clássicos como doji e engolfo, algumas formações menos conhecidas também podem indicar mudanças sutis na psicologia do mercado. O padrão "bebê abandonado" (Abandoned Baby) é uma rara formação de reversão composta por três barras: um doji fica separado do candle anterior por um gap e, em seguida, outro gap na direção oposta deixa as três barras "isoladas" umas das outras, frequentemente sinalizando uma forte reversão da tendência. O padrão "sanduíche" (Stick Sandwich) se forma quando dois candles fortes da mesma cor envolvem uma barra pequena de cor oposta, sendo que a última barra fecha no mesmo nível de fechamento da primeira, ou muito próximo dele. Essa formação indica que, apesar de um breve movimento contrário à tendência, o impulso original permanece. Já o padrão "gap de Tasuki" (Tasuki Gap) é uma formação de continuação composta por três barras, na qual o gap inicial, formado na direção da tendência, é parcialmente preenchido pela terceira barra, mas não chega a ser completamente fechado. Isso evidencia a resistência do mercado em fechar esse gap e confirma a força da tendência atual.

Outros dois padrões menos conhecidos, "Hikkake" e "linha de cinturão" (Belt Hold), acrescentam nuances importantes à análise. O padrão "Hikkake", cujo nome significa "armadilha" em japonês, começa com uma barra interna, seguida de um falso rompimento que atrai traders para uma determinada direção. Em seguida, o preço reverte e fecha além do limite oposto, deixando presos os participantes que se posicionaram no lado errado do mercado e acelerando a tendência principal. Já o padrão "linha de cinturão" (Belt Hold) é formado por um único candle forte, que se forma na direção oposta ao movimento anterior dos preços. Na versão de alta, o candle abre na mínima ou próximo dela durante uma tendência de baixa e fecha significativamente acima da abertura. Na versão de baixa, abre na máxima ou próximo dela durante uma tendência de alta e fecha acentuadamente abaixo da abertura. Como o Belt Hold representa uma mudança brusca no controle do mercado dentro de uma única barra, sem exigir que o candle anterior seja engolfado, ele pode fornecer um sinal claro e forte de reversão em níveis importantes de suporte e resistência.

Na TA-Lib, todas as funções de reconhecimento de padrões de candles começam com o prefixo CDL, abreviação de "Candle". Esse prefixo indica que a função implementa um dos algoritmos clássicos de reconhecimento de padrões, como CDLDOJI, CDLENGULFING, CDLHAMMER, entre outros. Ao chamar uma dessas funções CDL* com arrays de preços Open-High-Low-Close (OHLC), ela retorna uma sequência de valores inteiros. Os valores diferentes de zero indicam a presença do padrão correspondente, enquanto o sinal do valor indica sua direção.



Análise do funcionamento do sistema

Nesta seção, vou explicar detalhadamente como o sistema funciona, destacando separadamente as funções dos scripts MQL5 e Python. A arquitetura se baseia em um ciclo bem definido de requisição e resposta: sempre que um novo candle é fechado, o EA MQL5 coleta os preços de abertura, máxima, mínima e fechamento, além das marcas de tempo das últimas 60 barras. Em seguida, reúne esses dados em um pacote JSON compacto e o envia ao serviço Flask local, por meio do endpoint /patterns.

O EA então aguarda a resposta do servidor, que agora contém dois arrays paralelos, cada um com 60 elementos: o primeiro contém os nomes dos padrões de candles, com uma entrada correspondente a cada barra, enquanto o segundo contém os sinais correspondentes, "bullish" ou "bearish". Assim que recebe a resposta, o EA faz o parsing do JSON. No MetaTrader 5, ele primeiro remove todos os rótulos existentes e depois recria os objetos de texto na máxima de cada barra em que foi identificado um padrão válido. Os rótulos são exibidos em verde para sinais de alta e em vermelho para sinais de baixa. Por fim, o EA chama a função Alert().

Enquanto isso, o endpoint Flask recebe o JSON, organiza os dados em um DataFrame do pandas, aplica mais de 60 funções de detecção de padrões da TA-Lib, atribui um rótulo a cada barra, identifica a direção de alta ou de baixa dos sinais e retorna os dois arrays, além de um log resumido e uma métrica de tempo de execução. Essa divisão de responsabilidades mantém cada parte do sistema especializada, eficiente e fácil de ampliar: o MQL5 cuida da entrada e saída de dados no gráfico e dos alertas, enquanto o Python fica responsável pela padronização da estrutura dos dados e pela lógica de reconhecimento de padrões.

  • Análise do EA em MQL5

Na primeira inicialização, o EA registra imediatamente uma mensagem no log, lembrando que é necessário adicionar o endpoint Flask à lista de URLs permitidas nas configurações dos EAs do MetaTrader 5. Ao realizar essa configuração uma única vez, você garante que as requisições HTTP subsequentes ao serviço local de reconhecimento de padrões sejam autorizadas, evitando falhas silenciosas durante a execução.

int OnInit()
{
   Log("EA started – allow WebRequest to: " + InpURL);
   return INIT_SUCCEEDED;
}

A cada tick recebido, o EA compara a marca de tempo do candle atual, no índice 0, com a da última barra processada. Se nenhuma nova barra tiver sido fechada desde a verificação anterior, a função simplesmente encerra sua execução, evitando operações desnecessárias e garantindo que a análise de padrões seja realizada apenas uma vez para cada candle fechado.

void OnTick()
{
   datetime bar = iTime(_Symbol, InpTF, 0);
   if(bar == 0 || bar == g_lastBar)
      return;
   g_lastBar = bar;
   // … (continue processing)
}

Assim que uma nova barra é confirmada, o EA percorre os últimos 60 candles em ordem inversa, extraindo os preços de abertura, máxima, mínima e fechamento, além da marca de tempo UNIX de cada barra. Esses valores são armazenados temporariamente em arrays de tamanho fixo e depois reunidos em um objeto JSON compacto, que contém o símbolo, o timeframe e os 60 conjuntos de dados OHLC com suas respectivas marcas de tempo.

double o[BARS], h[BARS], l[BARS], c[BARS];
long   t[BARS];
for(int i = 0; i < BARS; i++)
{
   int sh = i + 1;
   o[i] = iOpen(_Symbol, InpTF, sh);
   h[i] = iHigh(_Symbol, InpTF, sh);
   l[i] = iLow(_Symbol, InpTF, sh);
   c[i] = iClose(_Symbol, InpTF, sh);
   t[i] = (long)iTime(_Symbol, InpTF, sh);
}
string json = StringFormat(
   "{\"symbol\":\"%s\",\"timeframe\":%d,\"time\":[%s],\"open\":[%s],\"high\":[%s],\"low\":[%s],\"close\":[%s]}",
   _Symbol, InpTF, CSVInt(t), CSV(o), CSV(h), CSV(l), CSV(c)
);
Log("JSON-OUT: " + json);

Depois de montar o objeto JSON, o EA envia uma requisição POST à URL configurada do serviço Flask e aguarda a resposta até o timeout definido. Ele captura possíveis erros de rede ou de acesso e registra mensagens detalhadas no log caso a requisição falhe. Se a requisição for bem-sucedida, recebe a resposta JSON do servidor como uma string para processamento posterior.

char body[];
StringToCharArray(json, body, 0, StringLen(json));
char reply[];
string hdr = "Content-Type: application/json\r\n", respHdr;
int code = WebRequest("POST", InpURL, hdr, InpTimeout, body, reply, respHdr);
if(code == -1)
{
   Log("WebRequest failed: " + IntegerToString(GetLastError()));
   return;
}
string resp = CharArrayToString(reply, 0, -1, CP_UTF8);
Log("HTTP " + IntegerToString(code) + " RESP: " + resp);

Após a conclusão da chamada HTTP, o EA procura na resposta os dois arrays: "patterns" e "signals". Para converter esses arrays JSON em arrays de strings do MQL5, utiliza rotinas simples de busca de trechos na string de resposta e de divisão de seu conteúdo. Antes de prosseguir, verifica se cada array contém exatamente 60 elementos.

string patTxt, sigTxt, patt[], sigs[];
if(!ExtractArray(resp, "patterns", patTxt) ||
   !ExtractArray(resp, "signals", sigTxt) ||
   !ParseArray(patTxt, patt) ||
   !ParseArray(sigTxt, sigs) ||
   ArraySize(patt) != BARS ||
   ArraySize(sigs) != BARS)
{
   Log("Malformed patterns or signals");
   return;
}

Para cada uma das 60 barras, o EA verifica os valores correspondentes de padrão e sinal. Sempre que identifica um padrão válido, ou seja, diferente de "None", calcula o índice de deslocamento (shift) da barra correspondente, remove as marcações anteriores e cria um novo rótulo de texto na máxima da barra. Os rótulos são exibidos em verde-limão para sinais de alta e em vermelho para sinais de baixa. A criação de cada rótulo também aciona um alerta do MetaTrader 5, contendo o símbolo, o timeframe, a direção do sinal, o nome do padrão e a marca de tempo.

for(int i = 0; i < BARS; i++)
{
   string pat = patt[i], sig = sigs[i];
   if(pat == "" || pat == "None") continue;

   int shift = BARS - i;
   datetime tm = iTime(_Symbol, InpTF, shift);
   double y = iHigh(_Symbol, InpTF, shift);
   string obj = PREFIX + IntegerToString(shift);
   ObjectCreate(0, obj, OBJ_TEXT, 0, tm, y);
   ObjectSetString(0, obj, OBJPROP_TEXT, pat);

   color col = (sig == "bullish" ? clrLime : clrRed);
   ObjectSetInteger(0, obj, OBJPROP_COLOR, col);
   ObjectSetInteger(0, obj, OBJPROP_FONTSIZE, InpFontSize);
   ObjectSetInteger(0, obj, OBJPROP_SELECTABLE, false);

   Alert(StringFormat("%s %s %s pattern '%s' at %s",
                      _Symbol, EnumToString(InpTF), sig, pat,
                      TimeToString(tm, TIME_DATE|TIME_MINUTES)));
}

Quando o EA é removido ou o MetaTrader 5 é encerrado, a rotina de desinicialização percorre todos os objetos do gráfico cujos nomes começam com o prefixo utilizado nos rótulos do EA e os exclui. Isso garante que nenhuma marcação antiga permaneça no gráfico após a desativação do EA, mantendo a área de trabalho limpa.

void OnDeinit(const int reason)
{
   for(int i = ObjectsTotal(0,0,-1)-1; i >= 0; i--)
   {
      string n = ObjectName(0, i, 0, -1);
      if(StringFind(n, PREFIX) == 0)
         ObjectDelete(0, n);
   }
   Log("EA removed");
}

  • Análise do servidor Flask em Python

Quando o serviço Flask é iniciado, ele configura o nível de logging como INFO para registrar todas as requisições recebidas e os eventos internos, tanto no console quanto em um arquivo de log com rotação. Logo depois, o script percorre a biblioteca TA-Lib e reúne dinamicamente, em um dicionário mantido na memória, todas as funções de reconhecimento de padrões de candles cujos nomes começam com "CDL". Dessa forma, o serviço oferece suporte automático a todos os padrões atuais e futuros da TA-Lib, sem necessidade de alterações manuais no código. Assim que a biblioteca é atualizada com novos padrões, eles passam a ficar disponíveis no sistema.

app = Flask(__name__)
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(levelname)s %(message)s')
app.logger.setLevel(logging.INFO)

# load all TA‑Lib candlestick functions
CDL_FUNCS = {
    name: getattr(talib, name)
    for name in talib.get_functions() if name.startswith("CDL")
}

Sempre que o EA envia uma requisição POST ao endpoint /patterns, o servidor lê o corpo bruto da requisição, remove os bytes nulos que podem ser acrescentados pelo MetaTrader 5 e tenta fazer o parsing do conteúdo JSON. Se o parsing falhar ou algum dos arrays obrigatórios, open, high, low, close ou time, estiver ausente, o servidor retorna imediatamente um erro HTTP 400 acompanhado de uma mensagem explicativa. Essa validação inicial impede que dados incorretos ou incompletos avancem pelo pipeline e provoquem erros difíceis de identificar nas etapas seguintes.

@app.route('/patterns', methods=['POST'])
def patterns():
    app.logger.info("Received /patterns request")
    try:
        raw = request.data
        if b'\x00' in raw:
            raw = raw.split(b'\x00', 1)[0]
        data = json.loads(raw.decode('utf-8'))
    except Exception as e:
        return jsonify(error="Invalid JSON", details=str(e)), 400

Após validar o JSON, o servidor extrai os cinco arrays principais, correspondentes às marcas de tempo e às quatro séries de preços, e inverte sua ordem para que o índice 0 corresponda à barra mais antiga. Em seguida, converte as marcas de tempo UNIX em um DatetimeIndex do pandas, permitindo que as operações posteriores considerem corretamente as informações temporais. Se os arrays tiverem tamanhos diferentes ou contiverem valores não numéricos, o serviço registra imediatamente o problema e retorna um erro. Assim, apenas dados com tamanhos consistentes e tipos corretos chegam à lógica de reconhecimento de padrões.

try:
    symbol = data.get('symbol', 'Instrument')
    ts     = data.get('time', [])
    open_  = np.array(data['open'][::-1],  dtype=float)
    high   = np.array(data['high'][::-1],  dtype=float)
    low    = np.array(data['low'][::-1],   dtype=float)
    close  = np.array(data['close'][::-1], dtype=float)
    idx    = pd.to_datetime(np.array(ts[::-1], dtype='int64'), unit='s')
    app.logger.info(f"Loaded {len(open_)} bars for {symbol}")
except KeyError as ke:
    return jsonify(error=f"Missing field {ke}"), 400
except Exception as e:
    return jsonify(error="Bad field format", details=str(e)), 400

Com os arrays NumPy devidamente validados, o serviço percorre todas as funções de reconhecimento de padrões de candles da TA-Lib e aplica cada uma à série completa. Qualquer resultado diferente de zero é registrado como uma possível ocorrência do padrão correspondente. Duas rotinas auxiliares adicionais, uma para a validação complementar do padrão harami e outra para refinar o reconhecimento dos padrões de engolfo, são executadas conforme os flags definidos na requisição, aplicando critérios mais rigorosos quando necessário. Como cada chamada à TA-Lib é protegida por um bloco try/except, uma função que apresente problemas ou uma estrutura dimensional inesperada dos arrays não provoca a falha de todo o serviço. Nesses casos, o sistema simplesmente ignora a função problemática e continua processando as demais.

n = len(open_)
all_hits = [[] for _ in range(n)]
for name, func in CDL_FUNCS.items():
    try:
        res = func(open_, high, low, close)
    except Exception:
        continue
    for i, v in enumerate(res):
        if v == 0:
            continue
        if name == "CDLHARAMI" and fh and not is_manual_harami(open_, close, i, tol=HARAMI_TOL):
            continue
        if name == "CDLENGULFING" and fe and not is_manual_engulfing(open_, close, i, tol=ENGULF_TOL):
            continue
        all_hits[i].append((name, v))

Como vários padrões podem ser detectados na mesma barra, o serviço utiliza uma pequena lista configurável de prioridades para resolver conflitos e selecionar o padrão predominante. Por exemplo, os padrões de engolfo têm prioridade sobre os de harami, que, por sua vez, têm prioridade sobre o doji. Se nenhum dos padrões prioritários for identificado, o sistema seleciona aquele cujo resultado retornado pela TA-Lib apresenta o maior valor absoluto. Em seguida, o sinal algébrico do resultado selecionado, positivo ou negativo, é associado ao rótulo "bullish" ou "bearish". Essa abordagem determinística, que seleciona um único padrão por barra, permite atribuir um sinal claro a cada candle com um padrão identificado, simplificando a posterior inclusão dos rótulos no gráfico e a lógica de acionamento dos alertas.

PRIORITY = ["CDLENGULFING", "CDLHARAMI", "CDLDOJI"]
detected = [None] * n
signals  = [None] * n
for i, hits in enumerate(all_hits):
    if not hits:
        continue
    pick = next(((nm, val) for pat in PRIORITY for nm, val in hits if nm == pat), None)
    if pick is None:
        pick = max(hits, key=lambda x: abs(x[1]))
    nm, val = pick
    detected[i] = nm
    signals[i]  = "bullish" if val > 0 else "bearish"

Em seguida, o script cria um DataFrame do pandas a partir dos quatro arrays de preços e do índice de datas e horários, acrescentando duas novas colunas: uma para o nome do padrão identificado e outra para o sinal "bullish" ou "bearish". Para preparar a representação visual dos sinais, o script calcula um pequeno afastamento proporcional à amplitude total dos preços e cria uma lista de sobreposições gráficas com marcadores. As setas verdes apontadas para cima são posicionadas um pouco abaixo das mínimas das barras com sinais de alta, enquanto as setas vermelhas apontadas para baixo ficam ligeiramente acima das máximas das barras com sinais de baixa. Esse posicionamento, ajustado à amplitude dos preços exibidos no gráfico, ajuda a reduzir a poluição visual e mantém a apresentação consistente entre diferentes instrumentos e timeframes.

df = pd.DataFrame({"Open": open_, "High": high, "Low": low, "Close": close}, index=idx)
df["Pattern"] = pd.Series(detected, index=idx).fillna("None")
df["Signal"]  = pd.Series(signals,  index=idx).fillna("")

df.sort_index(inplace=True)
adds = []
price_rng = df["High"].max() - df["Low"].min()
for tstamp, row in df.iterrows():
    if row["Pattern"] == "None":
        continue
    if row["Signal"] == "bullish":
        y, marker, color = row["Low"] - price_rng*0.005, "^", "green"
    else:
        y, marker, color = row["High"] + price_rng*0.005, "v", "red"
    adds.append(mpf.make_addplot(
        [y if i == tstamp else np.nan for i in df.index],
        type="scatter", marker=marker, markersize=80, color=color
    ))

Por fim, o serviço define um esquema de cores personalizado para os candles, utilizando, por exemplo, verde para barras de alta e vermelho para barras de baixa. Em seguida, gera um gráfico completo de candles com o mplfinance, incluindo os marcadores preparados anteriormente. Para facilitar a leitura, adiciona uma legenda que explica tanto a direção dos candles quanto os marcadores dos padrões. O tamanho da figura é ajustado para melhorar a nitidez, e o gráfico é salvo em um arquivo PNG com nome exclusivo no diretório do servidor. Depois disso, a figura é fechada para liberar memória. A resposta JSON retorna as listas de padrões e sinais em ordem inversa, de modo que o índice 0 volte a corresponder à barra mais recente. Ela também inclui um resumo opcional do log e o nome do arquivo do gráfico salvo, que pode ser utilizado imediatamente pelo EA ou integrado a outras ferramentas de análise.

mc = make_marketcolors(up='green', down='red', edge='inherit', wick='inherit')
style = make_mpf_style(marketcolors=mc, base_mpf_style='default')
fig, axes = mpf.plot(df, type="candle", style=style,
                     title=f"{symbol} Patterns", addplot=adds,
                     volume=False, returnfig=True, tight_layout=True)
axes[0].legend(handles=[
    Line2D([0],[0],color='green',lw=4,label='Buy Candle'),
    Line2D([0],[0],color='red',lw=4,label='Sell Candle'),
    Line2D([0],[0],marker='^',linestyle='None',color='green',markersize=12,label='Buy Signal'),
    Line2D([0],[0],marker='v',linestyle='None',color='red',markersize=12,label='Sell Signal')
], loc='upper left', frameon=True)

fname = f"pattern_chart_{uuid.uuid4().hex[:8]}.png"
fig.savefig(path := os.path.join(os.path.dirname(__file__), fname), dpi=100)
plt.close(fig)
app.logger.info(f"Chart saved: {fname}")

return jsonify(
    patterns=[p or "None" for p in detected[::-1]],
    signals =[s or "none" for s in signals [::-1]],
    log=[],
    chart=fname
)


Testes e resultados

A seguir, apresentamos os resultados dos testes.

Teste com o Step Index

Logs do MQL5

A aba "Experts" do MetaTrader 5 exibe os logs com os dados enviados ao Python, os padrões recebidos do serviço Python e o nome de cada padrão detectado na barra atual. Essas informações são atualizadas sempre que uma nova barra é fechada, e cada atualização é acompanhada de um alerta.

2025.07.15 22:56:50.294 Candlestick Label 2 (Step Index,M15)    [CSLAB] JSON-OUT: {"symbol":"Step Index","timeframe":0,"time":[1752558300,1752559200,
1752560100,1752561000,1752561900,1752562800,1752563700,1752564600,1752565500,1752566400,1752567300,1752568200,
1752569100,1752570000,1752570900,1752571800,1752572700,1752573600,1752574500,1752575400,1752576300,1752577200,1752578100,1752579000,
1752579900,1752580800,1752581700,1752582600,1752583500,1752584400,1752585300,1752586200,1752587100,1752588000,1752588900,1752589800,
1752590700,1752591600,1752592500,1752593400,17525943
2025.07.15 22:56:50.804 Candlestick Label 2 (Step Index,M15)    [CSLAB] HTTP 200 RESP: {"chart":"pattern_chart_e06f7a61.png","log":[],"patterns":
["CDLENGULFING","CDLHARAMI","None","CDLCLOSINGMARUBOZU","CDLHARAMI","CDLHIKKAKE","CDLENGULFING","CDLHIGHWAVE","None","CDLLONGLINE",
"CDLHIGHWAVE","CDLSHORTLINE","CDLSHORTLINE","CDLHIKKAKE","CDLHARAMI","CDLENGULFING","CDLDOJI","None","None","None","CDLCLOSINGMARUBOZU",
"None","CDLBELTHOLD","None","CDLHARAMI","CDLBELTHOLD","CDLHIKKAKE","CDLENGULFING","CDLHIKKAKE","CDLHIKKAKE","None","CDLDOJI","CDLHAMMER",
"CDLHARAMI","CDLENGULFING"
2025.07.15 22:56:50.804 Candlestick Label 2 (Step Index,M15)    
2025.07.15 22:56:50.804 Candlestick Label 2 (Step Index,M15)    Alert: Step Index PERIOD_CURRENT bullish pattern 'CDLENGULFING' at 2025.07.15 05:45

Logs da linha de comando

  • POST /patterns: o cliente, ou seja, o EA MQL5, enviou uma requisição HTTP POST ao endpoint /patterns.
  • HTTP/1.1: a requisição utilizou o protocolo HTTP 1.1.
  • 200: o servidor retornou o código de status 200, correspondente a "OK", indicando que o endpoint processou a requisição com sucesso.

2025-07-15 22:54:52,246 INFO Received /patterns request
2025-07-15 22:54:52,250 INFO Loaded 60 bars for Step Index
2025-07-15 22:54:52,808 INFO Chart saved: pattern_chart_a77f2eec.png
2025-07-15 22:54:52,810 INFO 127.0.0.1 - - [15/Jul/2025 22:54:52] "POST /patterns HTTP/1.1" 200 -
2025-07-15 22:55:00,040 INFO Received /patterns request

A seguir, são apresentados os gráficos dos mesmos padrões identificados, ilustrando o comportamento do Step Index ao longo do tempo. Os gráficos foram gerados com Matplotlib, TA-Lib e mplfinance, mostrando tanto as barras de preços quanto os sinais dos padrões de candles detectados pelo nosso sistema:

Candles

  • Os corpos verdes representam os períodos em que o preço de fechamento ficou acima do preço de abertura, caracterizando candles de alta.
  • Os corpos vermelhos representam os períodos em que o preço de fechamento ficou abaixo do preço de abertura, caracterizando candles de baixa.

Sinais

  • Os marcadores verdes abaixo das barras indicam sinais de compra, correspondentes a padrões de alta.
  • Os marcadores vermelhos acima das barras indicam sinais de venda, correspondentes a padrões de baixa.

Teste com o Crash 1000 Index

Logs do MQL5

2025.07.15 23:30:00.299 Candlestick Label 2 (Crash 1000 Index,M15)      [CSLAB] JSON-OUT: {"symbol":"Crash 1000 Index","timeframe":0,"time":[1752561000,1752561900,
1752562800,1752563700,1752564600,1752565500,1752566400,1752567300,1752568200,1752569100,1752570000,1752570900,1752571800,1752572700,1752573600,1752574500,1752575400
,1752576300,1752577200,1752578100,1752579000,1752579900,1752580800,1752581700,1752582600,1752583500,1752584400,1752585300,1752586200,1752587100,1752588000,1752588900,
1752589800,1752590700,1752591600,1752592500,1752593400,1752594300,1752595200,1752596100,17
2025.07.15 23:30:02.749 Candlestick Label 2 (Crash 1000 Index,M15)      [CSLAB] HTTP 200 RESP: {"chart":"pattern_chart_ebd72b47.png","log":[],"patterns":
["CDLCLOSINGMARUBOZU","CDLBELTHOLD","CDLBELTHOLD","None","CDLBELTHOLD","CDLHIKKAKE","CDLENGULFING","CDLBELTHOLD","CDLHARAMI","CDLENGULFING","None","CDLBELTHOLD",
"None","CDLBELTHOLD","CDLBELTHOLD","CDLBELTHOLD","None","CDLBELTHOLD","CDLBELTHOLD","None","CDLBELTHOLD","CDLBELTHOLD","CDLBELTHOLD","CDLDOJI","CDLBELTHOLD","None",
"CDLSHORTLINE","CDLSHORTLINE","None","CDLENGULFING","CDLHARAMI","CDLCLOSINGMARUBOZU","None","CDLMATCHINGL
2025.07.15 23:30:02.749 Candlestick Label 2 (Crash 1000 Index,M15)      
2025.07.15 23:30:02.769 Candlestick Label 2 (Crash 1000 Index,M15)      Alert: Crash 1000 Index PERIOD_CURRENT bearish pattern 'CDLCLOSINGMARUBOZU' at 2025.07.15 06:30

Logs da linha de comando

2025-07-15 23:30:02,719 INFO Chart saved: pattern_chart_ebd72b47.png
2025-07-15 23:30:02,735 INFO 127.0.0.1 - - [15/Jul/2025 23:30:02] "POST /patterns HTTP/1.1" 200 -

Gráfico de padrões de candles gerado pelo serviço Python

Esse gráfico permite visualizar claramente os sinais produzidos por cada padrão identificado e o comportamento dos preços após sua ocorrência.


Conclusão

Nessa arquitetura, o MetaTrader 5 fica responsável pelas funções em que se destaca: obtenção de dados de mercado em tempo real, gerenciamento de objetos gráficos e acionamento de alertas integrados. Enquanto isso, um serviço Flask leve, que utiliza TA-Lib e mplfinance, assume todos os cálculos relacionados aos padrões de candles e a geração de gráficos com uma apresentação clara. Ao enviar 60 barras com dados OHLC e marcas de tempo em formato JSON, o EA recebe os resultados da análise em frações de segundo, sem sobrecarregar sua própria execução. O backend Python, por sua vez, retorna tanto os rótulos detalhados dos padrões quanto os gráficos com anotações, prontos para visualização ou compartilhamento.

Essa divisão clara de responsabilidades torna o sistema verdadeiramente modular: é possível aprimorar as regras de reconhecimento de padrões, ajustar as tolerâncias dos filtros de sinais e ampliar a lógica de alertas do EA de maneira independente, sem o risco de provocar efeitos colaterais indesejados. O empacotamento do serviço Flask em um contêiner Docker para uma implantação previsível, por exemplo, ou sua disponibilização por meio de um gateway de API com autenticação aumenta ainda mais a portabilidade e a segurança.

Recomendo aplicar essa abordagem aos instrumentos financeiros de sua preferência, experimentar sobreposições personalizadas de indicadores e integrar os gráficos gerados a plataformas de análise mais avançadas. Com essa arquitetura híbrida, você aproveita os pontos fortes tanto do MQL5 quanto das bibliotecas modernas do Python, transformando a análise manual de gráficos em um processo automatizado e preciso.





  
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 ToolQuarters Board
ZigZag AnalyzerCorrelation PathfinderMarket Structure Flip Detector Tool
Correlation Dashboard Currency Strength Meter
PAQ Analysis Tool
Dual EMA Fractal Breaker
Pin bar, Engulfing and RSI divergence
Liquidity SweepOpening Range Breakout ToolBoom and Crash InterceptorCCI Zer-Line EA
Candlestick RecognitionCandlestick Detection using TA-Lib    

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

Arquivos anexados |
candlesticks.py (6.76 KB)
Repensando as estratégias clássicas (Parte 14): Análise de múltiplas estratégias Repensando as estratégias clássicas (Parte 14): Análise de múltiplas estratégias
Neste artigo, continuamos a construção de um ensemble de estratégias de trading, utilizando o otimizador genético do MT5 para ajustar os parâmetros das estratégias. Hoje, analisaremos os dados em Python para verificar se esse tipo de modelo consegue prever melhor qual estratégia terá o melhor desempenho e qual delas operará com maior precisão, além de avaliar se essa abordagem é mais eficaz do que prever diretamente os retornos. Adianto que os testes do aplicativo com esse modelo estatístico revelaram uma queda acentuada no desempenho. O problema está no otimizador genético: infelizmente, ele favorece estratégias correlacionadas. Por isso, vamos reformular a abordagem, mantendo fixos os pesos dos votos e concentrando a otimização nos parâmetros dos indicadores.
Ferramentas de trading em MQL5 (Parte 5): criação de uma faixa de cotações com rolagem contínua para monitorar símbolos em tempo real Ferramentas de trading em MQL5 (Parte 5): criação de uma faixa de cotações com rolagem contínua para monitorar símbolos em tempo real
Neste artigo, desenvolveremos uma faixa de cotações com rolagem contínua em MQL5 para monitorar vários instrumentos em tempo real, exibindo preços Bid, spreads e variações percentuais diárias com efeito de rolagem. Implementaremos opções de personalização de fontes, cores e velocidade de rolagem para destacar os movimentos dos preços e as tendências do mercado.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Criação de EAs que se auto-otimizam em MQL5 (Parte 9): Cruzamento duplo de médias móveis Criação de EAs que se auto-otimizam em MQL5 (Parte 9): Cruzamento duplo de médias móveis
Este artigo apresenta o desenvolvimento de uma estratégia de cruzamento duplo de médias móveis que utiliza sinais de um timeframe superior (D1) para determinar as entradas em um timeframe inferior (M15), enquanto os níveis de stop-loss são calculados com base em um timeframe intermediário (H4), utilizado para a gestão de risco. São introduzidas constantes do sistema, enumerações personalizadas e a lógica dos modos de seguimento de tendência e reversão à média, com ênfase na modularidade e na futura otimização por meio de um algoritmo genético. Essa abordagem oferece condições flexíveis de entrada e saída, buscando reduzir o atraso dos sinais e melhorar o timing das operações ao alinhar as entradas nos timeframes inferiores às tendências dos timeframes superiores.