English Русский 中文 Español Deutsch 日本語
preview
Arbitragem estatística com ações cointegradas (Parte 1): testes de cointegração de Engle-Granger e Johansen

Arbitragem estatística com ações cointegradas (Parte 1): testes de cointegração de Engle-Granger e Johansen

MetaTrader 5 — Sistemas de negociação |
17 2
Jocimar Lopes
Jocimar Lopes

Introdução

Este é o primeiro de dois artigos que mostram como utilizar a cointegração em estratégias de arbitragem estatística. Muitas pessoas acreditam, equivocadamente, que essas estratégias são fórmulas "mágicas" livres de risco ou algo praticamente inacessível ao trader de varejo comum. Essa percepção equivocada é alimentada pelas histórias sobre os resultados extraordinários alcançados pelos grandes participantes do mercado, pela busca incessante do "Santo Graal" por traders iniciantes e, em última análise, pela falta de compreensão do que realmente é a arbitragem estatística.

Quando bem desenvolvidas e acompanhadas de uma infraestrutura adequada para a execução de ordens, essas estratégias podem apresentar baixo risco e rentabilidade consistente no longo prazo. No entanto, como qualquer outra atividade de negociação no mercado, também exigem uma gestão de risco adequada. Vale reforçar: estratégias de arbitragem estatística não são fórmulas mágicas isentas de risco.

No artigo anterior, analisamos uma estratégia simples de trading de pares baseada em correlação, que apresentou bons resultados no backtest, mas fracassou completamente após duas semanas de operação em tempo real em uma conta demo. Esse provavelmente é um dos exemplos mais claros e clássicos de que os backtests, embora úteis para avaliar a viabilidade de uma estratégia, podem dizer muito pouco sobre seu desempenho nas condições reais de mercado. Além disso, o resultado demonstra claramente que a arbitragem estatística não está livre de riscos. Mesmo uma estratégia promissora exige uma gestão de risco rigorosa (que não implementamos em nosso protótipo) e um ambiente adequado para a execução de ordens, questões que estão no cerne deste artigo.

Por que os resultados na conta demo foram tão ruins em comparação com o backtest? Como podemos aprimorar nossas ferramentas e ir além do coeficiente de correlação, que apenas indica como dois ativos se movimentam em conjunto, para estabelecer uma base mais confiável de identificação de relações de longo prazo entre três ou mais ativos? E como podemos ir além do trading de pares simples e ampliar nossa carteira para incluir um grupo de ações do mesmo setor?


Sucesso no backtest, fracasso na prática

Recentemente, eu e meu parceiro participamos de uma conversa informal com uma trader acostumada a operar contratos de dólar na bolsa brasileira B3. Ela comentou que gostaria de ter os recursos necessários para experimentar "alguma estratégia de arbitragem estatística com commodities agrícolas". No entanto, como era uma trader discricionária tradicional, com formação em análise técnica e Price Action, mas sem conhecimentos aprofundados de matemática ou estatística, estava convencida de que a arbitragem estatística seria algo "fora do seu alcance". Suas palavras refletiam exatamente a crença generalizada que mencionamos no início: a ideia de que a arbitragem estatística é inacessível ao trader de varejo.

Para ir direto ao ponto, transformamos essa ideia em um desafio entre amigos: e se aproveitássemos nosso tempo livre para desenvolver um sistema automatizado de arbitragem estatística como exercício de aprendizado? Mas como fazer isso se nenhum de nós é um grande especialista em estatística ou matemática, nem dispõe do poder computacional necessário para competir com os grandes participantes do mercado? Fizemos o que qualquer pessoa em fase de aprendizado faria: começamos a pesquisar o assunto em fontes acadêmicas para entender o problema, examinamos as alternativas disponíveis no mercado e analisamos tanto os casos de sucesso quanto os fracassos.

Nossos primeiros passos nessa jornada foram dados com um artigo que publiquei recentemente aqui sobre o matemático e gestor de hedge fund Jim Simons e seu lendário fundo Medallion. Naquele artigo, utilizamos um EA bastante simples para ilustrar a arbitragem estatística entre XAUEUR e XAUUSD.

O gráfico abaixo apresenta os resultados do nosso backtest, publicados anteriormente.

Figura 1. Resultados do backtest do EA de trading de pares

Figura 1. Resultados do backtest do EA de trading de pares (artigo anterior)

Em seguida, para verificar se o EA seria viável em condições reais, deixamos essa versão bastante simples operando durante duas semanas em uma conta demo com spread zero e comissão. Os resultados foram catastróficos.

Figura 2. Histórico de operações do EA de trading de pares durante duas semanas na conta demo

Figura 2. Histórico de operações (ReportHistory) do EA de trading de pares durante duas semanas na conta demo

Causas prováveis

Quando tentamos descobrir por que uma estratégia de trading fracassou, é tentador começar pelo "ajuste forçado dos parâmetros". Mas vamos adotar uma abordagem mais sistemática e investigar as causas fundamentais do problema.

Foram apenas duas semanas ruins seguidas?

Será que simplesmente enfrentamos duas semanas desfavoráveis no mercado? Afinal, mesmo no backtest tivemos períodos sem lucro nem prejuízo e até pequenos drawdowns, não é?

Sim, mas nem mesmo nas nossas piores semanas registramos tantas perdas consecutivas. Nesse teste em tempo real, nossa maior sequência de operações vencedoras foi de apenas quatro, enquanto a maior sequência de perdas chegou a quarenta e duas! A média de operações vencedoras consecutivas foi de apenas uma, enquanto a média de perdas consecutivas chegou a cinco. Em resumo, estávamos perdendo em praticamente todas as operações!

Figura 3. Sequência de perdas do EA de trading de pares na conta demo

Figura 3. Sequência de perdas do EA de trading de pares na conta demo

Estava claro que havia algo fundamentalmente errado. As condições eram muito diferentes daquelas observadas no nosso backtest. Além disso, por definição, o trading de pares é uma estratégia neutra em relação ao mercado, ou seja, seu retorno não depende dos retornos de valores mobiliários específicos.
"O trading de pares, em sua forma mais simples, é uma estratégia de investimento neutra em relação ao mercado." [Gatev et al. 2006]

(O trading de pares, como a forma mais simples de arbitragem estatística, costuma ser neutro em relação ao mercado. No entanto, nossos estudos acadêmicos mostram que, em um contexto mais amplo, nem sempre essa neutralidade se mantém. Dependendo do método de hedge adotado, a arbitragem estatística pode apresentar exposição a riscos de mercado ou setoriais. Abordaremos essa questão ao desenvolver o módulo de gestão de risco e de capital do nosso sistema. Por enquanto, podemos considerar nossa estratégia simples de trading de pares neutra em relação ao mercado.)

Jim Simons ganhou enormes quantias de dinheiro enquanto os mercados despencavam durante a crise do subprime de 2008. O fundo Medallion não era o único hedge fund que recorria à arbitragem estatística. Então, por que eles ganhavam milhões enquanto outros gestores do setor eram "massacrados" pela crise? Ao que tudo indica, havia algo que faziam melhor do que os demais. Apesar do sigilo rigoroso em torno de suas operações, deixaram algumas pistas valiosas que hoje parecem óbvias, mas que os traders de varejo frequentemente ignoram. A equipe do Medallion sempre se apoiou em volumes enormes de dados de alta qualidade. Também investiu em capacidade computacional de ponta e em roteamento de ordens de alta velocidade.

Portanto, uma semana ruim provavelmente não explica o fracasso. Embora a arbitragem estatística seja neutra em relação ao mercado, ela também é extremamente sensível à qualidade dos dados e ao momento de execução das ordens.

Mudança no ambiente de negociação?

Será que o problema foi a troca de corretora e a diferença nos spreads? Em estratégias de trading de pares de curto prazo, com milhares de entradas por semana, o spread bid/ask pode facilmente se tornar um problema. Como você sabe, as corretoras de Forex normalmente são remuneradas pelo spread e/ou por comissões calculadas com base no volume negociado.

Mas podemos descartar o spread. No backtest, utilizei deliberadamente uma conta com spreads elevados, de aproximadamente 170 pips para o ouro e 50 pips para o XAUEUR, em média. Para o teste na conta demo, passei a utilizar uma conta com spread bruto (raw spread) e cobrança de comissão, na qual os spreads médios eram de aproximadamente 20 pips para o XAUUSD e 10 pips para o XAUEUR.

Como o spread era menor na conta demo, dificilmente poderia ser o responsável pelo fracasso.

Slippage?

A arbitragem costuma ser sensível ao momento de execução das operações, e nossa estratégia de trading de pares não é exceção. Vejamos o que diz a política de execução de ordens da nossa corretora de CFDs sobre o tempo de execução das ordens:

"Executamos a maioria das ordens automaticamente, com intervenção manual mínima. No entanto, em mercados voláteis, a execução pode ocorrer a um preço significativamente diferente do preço de compra ou venda cotado, ou do último preço de venda registrado no momento em que a ordem é enviada."

Por fim, chegamos ao "suspeito de sempre": o slippage. Enviávamos ordens de execução imediata a preço de mercado, mas alguém conseguia fechar essa diferença de preços antes de nós. Se esse era o problema, não havia nada de errado nisso. Esse tipo de "front-running" é legal. Na prática, faz parte das regras do jogo da arbitragem. Significa apenas que alguém dispõe de mais recursos e/ou de recursos melhores: hardware mais potente, servidores instalados em um data center próximo à corretora (colocation) ou simplesmente software otimizado para alto desempenho, enquanto nós utilizávamos um protótipo. Provavelmente estávamos perdendo essa corrida para vários participantes ao mesmo tempo, cada um com alguma dessas vantagens e alguns com todas elas. Em outras palavras, os grandes participantes do mercado estavam simplesmente dando uma surra no nosso modesto protótipo.

O manual do usuário do MetaTrader 5 informa que é possível simular atrasos nos backtests:

"O Testador de Estratégias permite simular atrasos de rede durante a execução do EA, reproduzindo condições de teste próximas às reais. Para isso, é introduzido um intervalo de tempo entre o envio de uma solicitação de negociação e sua execução no testador. Durante esse intervalo, o preço pode mudar. Isso permite avaliar como a velocidade de processamento das operações afeta os resultados da estratégia.

No modo de execução instantânea, também é possível testar como o EA reage a uma requote enviada pelo servidor de negociação. Se a diferença entre o preço solicitado e o preço de execução ultrapassar o desvio permitido (deviation) especificado na ordem, o EA receberá uma requote."

Realizamos esse teste configurando um atraso aleatório...

Figura 4. Configurações do Testador de Estratégias do MetaTrader 5 com simulação de atraso

Figura 4. Configurações do Testador de Estratégias do MetaTrader 5 com simulação de atraso

... e BINGO! Mesmo que o slippage não seja o único responsável pelo fracasso, nosso "suspeito de sempre" parece ser o principal culpado.

Figura 5. Gráfico de resultados após a execução do teste com simulação de atraso

Figura 5. Gráfico de resultados após a execução do teste com simulação de atraso

Poderíamos tentar definir no código um desvio máximo permitido, mas essa restrição comprometeria completamente nossa estratégia. Para que ela seja lucrativa, precisamos conseguir operar ao preço certo praticamente de imediato. Além disso, a configuração do desvio máximo geralmente está disponível apenas em contas profissionais, o que já nos afastaria do nosso objetivo principal: desenvolver um sistema de arbitragem estatística acessível ao trader de varejo comum.


O principal problema: a execução

A arbitragem permite obter lucro comprando e vendendo praticamente qualquer coisa: de torradeiras a tokens, de commodities a moedas e praticamente qualquer outra coisa. Podemos imaginar que essa prática surgiu quando as pessoas começaram a trocar mercadorias, muito antes da existência do dinheiro como o conhecemos hoje. O conceito é simples: se uma hipotética torradeira estiver subvalorizada no mercado A, talvez possamos comprá-la ali e vendê-la no mercado B, onde é negociada pelo "preço justo". Se a diferença de preços for suficiente para cobrir os custos de transação, podemos obter lucro com risco mínimo.

O problema é que a possibilidade de lucrar com risco mínimo atrai muita gente. Quanto maior a relação retorno/risco, mais pessoas querem aproveitar a oportunidade. Quando falamos de arbitragem nos mercados financeiros, envolvendo commodities e moedas, os lucros podem ser enormes, assim como o número de participantes tentando comprar um ativo subvalorizado. Cada um faz o possível para enviar sua ordem antes dos concorrentes. A necessidade de ser o primeiro a apresentar uma cotação e ter sua ordem executada acabou impulsionando o desenvolvimento da infraestrutura, dos métodos e das práticas de negociação de alta frequência (HFT). Como você provavelmente sabe, operar com HFT não é nada simples e muito menos barato.

O principal problema é que o trader de varejo comum não dispõe dos recursos necessários para superar os grandes participantes do mercado em velocidade. Mesmo deixando de lado a falta de uma equipe altamente qualificada de estatísticos e matemáticos, apoiada por uma enorme capacidade computacional para desenvolver modelos, ainda precisaríamos, no mínimo, de serviços de colocation (instalação de um servidor no data center da corretora), código de alto desempenho e uma corretora capaz de enviar e rotear ordens em poucos milissegundos...

Os resultados do backtest demonstraram a viabilidade teórica da estratégia em condições ideais de execução de ordens. Ou seja, nossas ordens eram executadas praticamente de imediato, sem atrasos de rede nem erros no servidor da corretora.

É fácil perceber que essa infraestrutura não está ao alcance do trader de varejo comum. Como pequenos investidores individuais, se quisermos aproveitar oportunidades de arbitragem nos mercados financeiros, precisamos encontrar alternativas ao nosso protótipo rudimentar de trading de pares. Precisamos investigar e identificar padrões e anomalias que não dependam tanto da velocidade de execução.


Da correlação à cointegração

Anteriormente, apresentamos o mercado financeiro como Jim Simons o descrevia: um enigma em constante transformação. Um enigma que exige dos participantes do mercado uma adaptação contínua para maximizar os lucros e minimizar as perdas. Construímos uma "carteira" mínima com dois ativos, XAUUSD e XAUEUR, e escolhemos como relação estatística de interesse o desvio em relação à média das cotações dos dois ativos, tomando o dólar americano como referência. O alargamento do spread e seu posterior retorno à média serviam, respectivamente, como sinais de entrada e saída.

Até aqui, nossa "carteira" de arbitragem estatística ainda é bastante limitada, assim como as relações estatísticas que analisamos. Além disso, como não temos controle sobre a velocidade de execução, tentar otimizá-la agora seria prematuro. Primeiro, precisamos aprimorar nossa capacidade de análise, em vez de dedicar tempo a algo que está fora do nosso alcance. Precisamos ampliar a carteira. Nossas ferramentas de varredura precisam identificar mais relações estatísticas e incorporar mais indicadores capazes de gerar sinais de negociação.

O critério que utilizamos para selecionar esses pares foi a correlação. Limitamos nossa busca aos principais pares de moedas e escolhemos o par com maior correlação no intervalo de tempo analisado. Agora, vamos incorporar a cointegração à seleção dos ativos da nossa carteira.

Segundo os estatísticos:

"A correlação capta apenas relações de curto prazo, enquanto a cointegração identifica relações de equilíbrio de longo prazo. Duas variáveis podem apresentar forte correlação sem serem cointegradas, e vice-versa." [Alexander, 2001]

O coeficiente de correlação indica até que ponto os preços de XAUUSD e XAUEUR tendem a se movimentar simultaneamente na mesma direção. Já o teste de cointegração identifica uma relação de equilíbrio entre os ativos que se mantém no longo prazo. Além disso, enquanto o coeficiente de correlação analisa apenas dois ativos por vez, os testes de cointegração não estão necessariamente sujeitos a essa limitação. Isso significa que podemos avaliar simultaneamente vários ativos candidatos à nossa carteira, tornando esses testes ideais para nossos objetivos.

Como traders ou desenvolvedores de EAs, não precisamos dominar toda a matemática por trás dos testes de cointegração. Como utilizaremos a integração do Python com o MetaTrader 5 para analisar os dados e selecionar os ativos da carteira, podemos recorrer a funções prontas de bibliotecas especializadas. Além disso, assistentes de IA (incluindo os GPTs integrados ao MetaTrader 5) podem nos ajudar a economizar tempo no desenvolvimento de scripts simples para essas tarefas.

Como traders, precisamos entender o que significa cointegração e como utilizá-la para identificar oportunidades, especialmente no mercado de ações, onde ela é reconhecidamente mais útil e eficaz do que no mercado cambial. Como desenvolvedores de EAs, precisamos saber utilizar as melhores bibliotecas disponíveis e interpretar seus resultados para realizar ciclos sucessivos de ajustes e aprimorar nosso sistema. Ao recorrer a bibliotecas de alta qualidade, e o ecossistema Python supera os demais na oferta de bibliotecas de matemática, estatística e aprendizado de máquina, economizamos tempo, evitamos erros e possivelmente obtemos um desempenho melhor.

Se quiser se aprofundar no assunto, recomendo consultar o livro de Carol Alexander citado anteriormente, especialmente o capítulo 12, inteiramente dedicado à cointegração. Nesse livro, a autora aborda desde a história do uso da cointegração nas finanças até suas aplicações em diferentes mercados, incluindo Forex. Trata-se de uma obra de referência bastante conhecida nessa área.

Vamos ver como aplicar os dois testes de cointegração mais recomendados, o teste de Engle-Granger e o teste de Johansen, a diferentes classes de ativos e como interpretar seus resultados.


Teste de cointegração de Engle-Granger

O teste de Engle-Granger pode ser utilizado para avaliar o grau de cointegração entre dois ativos. Seu objetivo é responder à seguinte pergunta: "Esses dois ativos compartilham uma relação de equilíbrio de longo prazo?" Do ponto de vista de traders e desenvolvedores de EAs, esse teste permite identificar se o spread entre os dois ativos apresenta reversão à média. No entanto, a resposta não é simplesmente binária ("sim" ou "não"). Assim como o coeficiente de correlação de Pearson, que analisamos anteriormente, o teste fornece um valor relativo (p-value). Podemos comparar esse valor com os resultados de outros pares para identificar qual deles apresenta maior grau de cointegração.

O p-value representa a probabilidade de observarmos esses dados supondo que NÃO exista cointegração. Portanto, quanto menor o p-value, melhor. Existem alguns limiares de referência amplamente utilizados: um p-value inferior a 0,05 costuma ser interpretado como um sinal moderado de cointegração; inferior a 0,01, como um sinal forte; e superior a 0,05, como um sinal de ausência de cointegração. Como sempre acontece no trading, é importante ajustar cuidadosamente esses limiares às necessidades específicas da sua estratégia.

Para executar os dois testes de cointegração em Python, utilizamos a conhecida biblioteca statsmodels:

"Um módulo Python que oferece classes e funções para estimar diversos modelos estatísticos, realizar testes estatísticos e explorar dados estatísticos."

from datetime import datetime, timedelta
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import coint
import matplotlib.pyplot as plt

# connect to MetaTrader 5 terminal
if not mt5.initialize(login=********, server="MetaQuotes-Demo",password="********"):
    print("initialize() failed, error code =",mt5.last_error())
    quit()

# Forex majors - check your Market Watch names
symbols = ['EURUSD', 'GBPUSD', 'USDJPY', 'AUDUSD', 'USDCAD', 'NZDUSD', 'USDCHF']

Nosso objetivo é encontrar o par com maior grau de cointegração entre as principais moedas. Não esperamos encontrar um nível elevado de cointegração; o objetivo, por enquanto, é apenas ilustrar o funcionamento do teste de Engle-Granger.

# define the timeframe and the number of days
timeframe = mt5.TIMEFRAME_D1  # Daily
n_days = 600
utc_to = datetime.now()
utc_from = utc_to - timedelta(days=n_days)

Utilizaremos as variáveis utc_from e utc_to para solicitar cotações referentes a um período de 600 dias, conforme definido nos parâmetros do script. Como referência, um ano tem aproximadamente 250 dias de negociação.

# download historical data for each symbol
data = {}

for symbol in symbols:
    # Make sure the symbol is available in Market Watch
    mt5.symbol_select(symbol, True)
    
    # Get historical rates
    rates = mt5.copy_rates_range(symbol, timeframe, utc_from, utc_to)
    
    if rates is None or len(rates) == 0:
        print(f"No data for {symbol}.")
        continue

    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    df.set_index('time', inplace=True)
    data[symbol] = df['close']

Nesse laço for, obtemos os preços de fechamento de cada símbolo, convertemos os dados em um DataFrame do Pandas e definimos o array time como índice da tabela.

Depois de concatenar os DataFrames e remover as linhas com valores ausentes, que poderiam distorcer os resultados, executamos o teste de cointegração.

# Store cointegration test results
results = []

# Test all unique pairwise combinations for cointegration
pairs = [(a, b) for i, a in enumerate(data.columns) for j, b in enumerate(data.columns) if i < j]

print("Cointegration test results (Engle-Granger):")
for a, b in pairs:
    score, pvalue, _ = coint(data[a], data[b])
    results.append((a, b, pvalue))
    print(f"{a} vs {b} | p-value: {pvalue:.4f}")
Index(['EURUSD', 'GBPUSD', 'USDJPY', 'AUDUSD', 'USDCAD', 'NZDUSD', 'USDCHF'], dtype='object')
Cointegration test results (Engle-Granger):

EURUSD vs GBPUSD | p-value: 0.3183
EURUSD vs USDJPY | p-value: 0.6990
EURUSD vs AUDUSD | p-value: 0.9308
EURUSD vs USDCAD | p-value: 0.9206
EURUSD vs NZDUSD | p-value: 0.9741
EURUSD vs USDCHF | p-value: 0.4342
GBPUSD vs USDJPY | p-value: 0.3273
GBPUSD vs AUDUSD | p-value: 0.7995
GBPUSD vs USDCAD | p-value: 0.6264
GBPUSD vs NZDUSD | p-value: 0.7810
GBPUSD vs USDCHF | p-value: 0.0238
USDJPY vs AUDUSD | p-value: 0.6299
USDJPY vs USDCAD | p-value: 0.5620
USDJPY vs NZDUSD | p-value: 0.6398
USDJPY vs USDCHF | p-value: 0.2377
AUDUSD vs USDCAD | p-value: 0.1260
AUDUSD vs NZDUSD | p-value: 0.2920
AUDUSD vs USDCHF | p-value: 0.5980
USDCAD vs NZDUSD | p-value: 0.0052
USDCAD vs USDCHF | p-value: 0.8574
NZDUSD vs USDCHF | p-value: 0.6384

Most cointegrated pair: USDCAD and NZDUSD (p = 0.0052)

Após identificar o par com maior grau de cointegração, podemos avaliar se vale a pena incluí-lo na nossa carteira de trading de pares. O gráfico abaixo mostra claramente que o spread reverte à média, mas a questão é: será que essa operação realmente compensa?

Figura 6. Gráfico do spread cointegrado entre USDCAD e NZDUSD

Figura 6. Gráfico do spread cointegrado entre USDCAD e NZDUSD com a média e dois desvios-padrão

A linha preta tracejada indica a média do spread, que representa seu nível de equilíbrio de longo prazo. As duas linhas vermelhas tracejadas marcam os limites de dois desvios-padrão. Esses limites estabelecem os limiares para identificar possíveis sinais de negociação quando o spread atinge níveis excepcionalmente altos ou baixos. Portanto, o número de desvios-padrão é um parâmetro importante a ser considerado na otimização da estratégia.

Coeficiente de hedge

Observe o valor -1,54 utilizado no cálculo do spread de longo prazo:

Spread = USDCAD - (-1.54) * NZDUSD

ou

USDCAD + 1.54 * NZDUSD

Esse valor é o coeficiente de hedge. Trata-se da proporção ideal, estimada estatisticamente por regressão linear, que pondera as cotações de USDCAD e NZDUSD para obter um spread estacionário, ou seja, oscile em torno de uma média constante. Esse é um elemento fundamental das estratégias de reversão à média. Ao utilizar esse coeficiente, colocamos os preços em uma escala comparável, levando em conta a volatilidade de cada ativo e a relação direcional entre eles.

O spread representa os desvios relativos entre os preços. Em termos de trading de pares, quando o spread ultrapassa +2 STD, ele está historicamente elevado, e podemos considerar a possibilidade de vender o spread. Por outro lado, quando cai abaixo de -2 STD, ele está historicamente baixo, e podemos considerar a possibilidade de comprá-lo.

Para montar um par neutro em relação ao mercado com base nesses dados, devemos vender simultaneamente 1,54 unidades de NZDUSD para cada unidade de volume de USDCAD que compramos (long), ou fazer o inverso, dependendo da direção do sinal. Quando o spread retorna à média, encerramos a operação fechando as duas posições.

Você pode utilizar esse mesmo código com símbolos de qualquer mercado ou classe de ativos. Para executá-lo sem outras modificações, basta substituir os nomes dos símbolos na parte superior do script.


Teste de cointegração de Johansen

O teste de cointegração de Johansen busca responder à mesma pergunta que o teste de Engle-Granger: esses ativos compartilham uma relação de equilíbrio de longo prazo? Na prática, para nossos objetivos, a principal diferença é que o teste de Johansen permite avaliar mais de dois ativos simultaneamente e determinar se esses N ativos podem formar um spread comum com reversão à média. Podemos utilizá-lo como uma ferramenta poderosa para investigar possíveis relações de cointegração entre ações de um mesmo setor, como empresas de petróleo e gás, gigantes da tecnologia e assim por diante. Isso nos permite identificar grupos de três, quatro ou mais ações (ou quaisquer outros ativos) que compartilham uma relação de equilíbrio de longo prazo.

O teste verifica se existem combinações de variáveis que sejam estacionárias (com reversão à média), mesmo quando as séries temporais originais não apresentam essa propriedade. Ele determina quantas relações de cointegração independentes existem entre as variáveis. Esse número é chamado de posto de cointegração (cointegration rank).

O teste fornece duas medidas estatísticas principais: a estatística do traço (Trace Statistic) e o valor crítico.

Para cada posto possível (0, 1, 2, ..., N-1, em que N é o número de séries), obtemos um valor da estatística do traço. Quanto maior esse valor, mais fortes são as evidências de cointegração. O valor crítico é o limiar com o qual comparamos a estatística do traço. Ele depende de:

  • Número de variáveis avaliadas;
  • Tamanho da amostra;
  • Nível de confiança (90%, 95% ou 99%).

Neste caso específico, o número de variáveis corresponde à quantidade de instrumentos financeiros (símbolos) incluídos no teste.

# Johansen Test
from datetime import datetime, timedelta
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import coint
from statsmodels.tsa.vector_ar.vecm import coint_johansen
import matplotlib.pyplot as plt
# Create a matrix for the test
log_prices = data.apply(np.log)

johansen_result = coint_johansen(log_prices, det_order=0, k_ar_diff=1)

# Trace statistics and critical values
print("\nJohansen Test Results (Trace Statistic):")
for i, stat in enumerate(johansen_result.lr1):
    cv = johansen_result.cvt[i, 1]  # 5% critical value
    print(f"Rank {i}: Trace Stat = {stat:.2f} | 5% CV = {cv:.2f} | {'Significant' if stat > cv else 'Not significant'}")

Para consultar os resultados em outros níveis de confiança, basta utilizar os índices correspondentes:
johansen_result.cvt[i, 0] para 90%
johansen_result.cvt[i, 2] para 99%

Considerando um nível de confiança de 95% (nível de significância de 5%), apresentamos abaixo os resultados obtidos para os principais pares de moedas, utilizando o mesmo período e o mesmo timeframe (D1) adotados no teste de Engle-Granger.

Index(['EURUSD', 'GBPUSD', 'USDJPY', 'AUDUSD', 'USDCAD', 'NZDUSD', 'USDCHF'], dtype='object')

Johansen Test Results (Trace Statistic):

Rank 0: Trace Stat = 105.25 | 5% CV = 125.62 | Not significant
Rank 1: Trace Stat = 68.10 | 5% CV = 95.75 | Not significant
Rank 2: Trace Stat = 42.01 | 5% CV = 69.82 | Not significant
Rank 3: Trace Stat = 25.83 | 5% CV = 47.85 | Not significant
Rank 4: Trace Stat = 11.73 | 5% CV = 29.80 | Not significant
Rank 5: Trace Stat = 5.74 | 5% CV = 15.49 | Not significant
Rank 6: Trace Stat = 0.58 | 5% CV = 3.84 | Not significant

Como esperado, não encontramos cointegração estatisticamente significativa entre os sete principais pares de moedas.

No entanto, ao testar duas ações conhecidas, Google e Nvidia, no timeframe H1 (veja abaixo), obtemos um resultado de cointegração estatisticamente significativo para o mesmo período e nível de confiança.

Index(['NVDA', 'GOOGL'], dtype='object')
Number of observations: 5769
Number of variables: 2

Johansen Test Results (Trace Statistic):
Rank 0: Trace Stat = 18.71 | 5% CV = 15.49 | Significant
Rank 1: Trace Stat = 0.29 | 5% CV = 3.84 | Not significant

Most cointegrated pair (Engle-Granger): NVDA and GOOGL | p-value: 0.0824

Figura 7. Gráfico do spread cointegrado entre NVDA e GOOGL

Figura 7. Gráfico do spread cointegrado entre NVDA e GOOGL com a média e dois desvios-padrão

Muito bem, segundo o teste de Johansen, existe cointegração significativa entre GOOGL e NVDA. Entretanto, o p-value do teste de Engle-Granger é superior a 0,05, indicando ausência de cointegração. Por que os resultados dos dois testes se contradizem? O que isso significa?

Vamos executar o mesmo script, apenas invertendo a ordem dos símbolos. Você perceberá uma diferença interessante entre os testes de Engle-Granger e Johansen.

Index(['GOOGL', 'NVDA'], dtype='object')
Number of observations: 5769
Number of variables: 2

Johansen Test Results (Trace Statistic):
Rank 0: Trace Stat = 18.71 | 5% CV = 15.49 | Significant
Rank 1: Trace Stat = 0.29 | 5% CV = 3.84 | Not significant

Most cointegrated pair (Engle-Granger): GOOGL and NVDA | p-value: 0.0403

Figura 8. Gráfico do spread cointegrado entre GOOGL e NVDA

Figura 8. Gráfico do spread cointegrado entre GOOGL e NVDA com a média e dois desvios-padrão

Como você pode observar, o teste de Engle-Granger é sensível à ordem dos ativos, enquanto os resultados do teste de Johansen não foram afetados por essa inversão. Se você estudar o teste de Johansen mais a fundo, descobrirá que, em determinadas situações, ele também pode apresentar sensibilidade à ordem das variáveis, especialmente com amostras pequenas ou quando os resultados estão próximos do limiar de significância estatística. No entanto, em termos teóricos, o teste geralmente é invariante à ordem. Se você perceber resultados contraditórios após inverter a ordem dos ativos, a primeira medida a tomar é aumentar o tamanho da amostra. Foi justamente por isso que substituímos o timeframe D1 (diário) pelo H1 (horário): para fornecer mais dados ao teste.

Como o teste de Engle-Granger é sensível à ordem das variáveis, devemos sempre executá-lo nas duas ordens possíveis e complementar a avaliação com uma análise visual dos gráficos, para confirmar se o spread realmente apresenta reversão à média.


Teste de estacionariedade do spread

O spread é uma série temporal e, para confirmar que apresenta reversão à média, precisamos testar sua estacionariedade. Isso significa verificar se sua média, variância e covariância permanecem constantes ao longo do tempo. Quando uma série temporal é estacionária, torna-se muito mais fácil modelá-la e fazer previsões utilizando modelos como o ARIMA.

A mesma biblioteca Python que utilizamos nos testes de cointegração oferece funções prontas para testar a estacionariedade: o teste aumentado de Dickey-Fuller (ADF) e o teste de Kwiatkowski-Phillips-Schmidt-Shin (KPSS).

from statsmodels.tsa.stattools import adfuller

(...)

adf_result = adfuller(spread)

ADF Test on Spread:
ADF Statistic : -3.0946
p-value : 0.0270
Critical Values:
1%: -3.4315
5%: -2.8620
10%: -2.5670

O spread é estacionário (a hipótese nula é rejeitada).

from statsmodels.tsa.stattools import kpss

(...)

def run_kpss(series, regression='c'):
    statistic, p_value, lags, crit_values = kpss(series, regression=regression, nlags='auto')

(...)

# Run KPSS test on the residuals
run_kpss(spread, regression='c')  # 'c' = test for level stationarity (use 'ct' for trend)

KPSS Test on Spread:
KPSS Statistic : 2.2702
p-value : 0.0100
Lags Used : 44
Critical Values: 10% : 0.347
5% : 0.463
2.5% : 0.574
1% : 0.739

❌ O spread NÃO é estacionário (a hipótese nula de estacionariedade é rejeitada).

Parece que encontramos novamente resultados contraditórios. O teste ADF indica que o spread é estacionário, enquanto o KPSS aponta o contrário. Mas não há motivo para preocupação! Os desenvolvedores da biblioteca statsmodels oferecem uma explicação clara de como interpretar qualquer combinação de resultados desses dois testes. Vou reproduzir um trecho da documentação do statsmodels porque, na minha opinião, seria difícil explicar isso de maneira mais concisa e compreensível.

"Caso 1: Ambos os testes indicam que a série não é estacionária. A série não é estacionária.

Caso 2: Ambos os testes indicam que a série é estacionária. A série é estacionária.

Caso 3: O KPSS indica estacionariedade, mas o ADF não. A série é estacionária em torno de uma tendência. É necessário remover essa tendência para tornar a série estritamente estacionária. Após a remoção da tendência, a série deve ser novamente submetida a um teste de estacionariedade.

Caso 4: O KPSS indica ausência de estacionariedade, enquanto o ADF indica estacionariedade. A série é estacionária em diferenças. Para torná-la estacionária, é necessário aplicar a diferenciação. A série resultante dessa operação deve ser novamente submetida a um teste de estacionariedade."

Agora sabemos que nosso spread é estacionário em diferenças, mas não estritamente estacionário. O que isso significa? Significa que a série temporal pode apresentar deriva, mas suas variações permanecem estáveis. Podemos transformá-la em uma série temporal estritamente estacionária simplesmente calculando a diferença entre valores consecutivos. Não por acaso, essa técnica é chamada de diferenciação. Ela permite remover tendências e sazonalidade, tornando a série temporal mais adequada para análise e previsão.

Precisamos tornar nosso spread estritamente estacionário? Muitos modelos de séries temporais exigem dados estacionários, mas esse não é o nosso caso. Na abordagem de cointegração que vamos implementar, não utilizaremos modelos de previsão.

Na verdade, não faremos previsões. Ainda assim, vale a pena saber que a análise de cointegração não se limita aos próprios testes de cointegração.


Conclusão

Neste artigo, avançamos mais uma etapa na construção do nosso framework de arbitragem estatística, voltado para o trader de varejo comum. Acrescentamos duas ferramentas ao nosso conjunto de recursos estatísticos para a composição da carteira: os testes de cointegração de Engle-Granger e Johansen. Vimos o que esperar de cada um deles e como interpretar seus resultados em um nível básico. Também aprendemos a testar a estacionariedade do spread utilizando o teste aumentado de Dickey-Fuller (ADF) e o teste de Kwiatkowski-Phillips-Schmidt-Shin (KPSS).

Este foi o primeiro de dois artigos dedicados à apresentação das ferramentas fundamentais. No próximo artigo, complementaremos esta introdução com a implementação prática, os backtests e a otimização de uma estratégia envolvendo um par de símbolos cambiais cointegrados e um grupo de ações cointegradas do mesmo setor.

Arrisco dizer que o mais importante aqui não é a apresentação das ferramentas, das funções ou mesmo dos exemplos de código. O principal aprendizado é que, se você é um trader de varejo, que não é matemático, estatístico nem desenvolvedor profissional, sua habilidade mais valiosa ao desenvolver uma estratégia de arbitragem estatística é compreender o ativo ou grupo de ativos com que está trabalhando.

Seu conhecimento dos mercados é inestimável, pois todo o trabalho complexo de matemática e estatística já foi feito para você por especialistas altamente qualificados. Os resultados desse trabalho estão disponíveis gratuitamente em diversas bibliotecas de código aberto, incluindo as bibliotecas MQL5 da plataforma MetaTrader 5. Também podemos contar com a ajuda valiosa de assistentes de IA para implementar essas soluções corretamente.

A lição que fica é simples: se seus recursos são limitados, evite estratégias que dependam da velocidade de execução e procure maneiras criativas de analisar os dados, aproveitando seu conhecimento dos mercados.

Referências bibliográficas

  • Alexander, C. (2001). Market Models: A Guide to Financial Data Analysis, Wiley.
  • Engle, R. F., & Granger, C. W. J. (1987). Co-integration and error correction: representation, estimation, and testing. Econometrica.
  • Gatev, E., Goetzmann, W. N., & Rouwenhorst, K. G. (2006). "Pairs Trading: Performance of a Relative-Value Arbitrage Rule." Review of Financial Studies.
  • Johansen, S. (1988). Statistical analysis of cointegration vectors. Journal of Economic Dynamics and Control.
Arquivo anexo Descrição
coint.ipynb Notebook Jupyter simplificado, com código Python para executar um único teste de cointegração de Engle-Granger em um par de instrumentos de negociação.
coint_googl_nvda.ipynb Notebook Jupyter mais completo, com código para executar os testes de Engle-Granger e Johansen em um número teoricamente ilimitado de instrumentos, além de testar a estacionariedade do spread.
helper_quotes_to_db.ipynb Arquivo auxiliar para salvar as cotações baixadas em um banco de dados SQLite3. Também é um notebook Jupyter com código Python. Seu objetivo é evitar downloads desnecessários e permitir trabalhar offline.

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

Arquivos anexados |
coint.ipynb (132.59 KB)
Últimos Comentários | Ir para discussão (2)
Zhuo Kai Chen
Zhuo Kai Chen | 9 jul. 2025 em 07:35
Artigo interessante! Já trabalhei em projetos semelhantes e também tive problemas na execução em ambiente de produção. :)
Cyberdude
Cyberdude | 9 dez. 2025 em 15:16
Zhuo Kai Chen #:
Artigo interessante! Já trabalhei em projetos semelhantes antes e também tive problemas na execução em ambiente de produção. :)
Também já experimentei muitas variações da Arbitragem Estatística e tentei por muito tempo criar um EA lucrativo. Mas, como sabemos, a execução é a maior desvantagem para os traders de varejo. É por isso que estou verdadeiramente convencido de que nenhum trader de varejo jamais conseguirá ter lucro com uma estratégia de Arbitragem Estatística. É simplesmente impossível.

Mas o artigo está bem escrito. Quem está começando nesse assunto vai conseguir acompanhá-lo facilmente.
Automatização de estratégias de negociação em MQL5 (Parte 22): Desenvolvimento de um sistema de recuperação por zonas para negociação a favor da tendência com o indicador Envelopes Automatização de estratégias de negociação em MQL5 (Parte 22): Desenvolvimento de um sistema de recuperação por zonas para negociação a favor da tendência com o indicador Envelopes
Desenvolveremos um sistema de recuperação por zonas (Zone Recovery System) integrado a uma estratégia de negociação a favor da tendência baseada no indicador Envelopes (Envelopes trend-trading strategy), utilizando MQL5. Também apresentaremos a arquitetura que combina os indicadores RSI e Envelopes para abrir operações e gerenciar a recuperação por zonas, com o objetivo de minimizar as perdas. Por meio da implementação prática e dos testes, demonstraremos como criar um sistema de negociação automatizada eficiente para mercados dinâmicos.
Implementação em MQL5 de módulos úteis de outras linguagens (Parte 1): Criação de uma biblioteca SQLite3 semelhante à do Python Implementação em MQL5 de módulos úteis de outras linguagens (Parte 1): Criação de uma biblioteca SQLite3 semelhante à do Python
O módulo sqlite3 do Python oferece uma maneira simples, rápida e prática de trabalhar com bancos de dados SQLite. Neste artigo, criaremos um módulo semelhante com base nas funções nativas do MQL5 para bancos de dados, com o objetivo de tornar o uso do SQLite3 em MQL5 tão simples e conveniente quanto em Python.
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.
Desenvolvimento de ferramentas para análise de Price Action (Parte 30): EA CCI Zero Line Desenvolvimento de ferramentas para análise de Price Action (Parte 30): EA CCI Zero Line
O futuro está na automação da análise do movimento dos preços. Neste artigo, combinamos o indicador Dual CCI (Commodity Channel Index, Índice do Canal de Commodities), a estratégia de cruzamento da linha zero (Zero Line Crossover), a EMA e a análise de Price Action para desenvolver uma ferramenta que gera sinais de negociação e define os níveis de stop-loss e take-profit com base no ATR. Leia este artigo para conhecer nossa abordagem ao desenvolvimento do EA CCI Zero Line.