Arbitraje estadístico mediante acciones cointegradas (Parte 1): Pruebas de cointegración de Engle-Granger y Johansen
Introducción
Este es el primero de dos artículos que explican cómo utilizar la cointegración en estrategias de arbitraje estadístico. Muchas personas consideran erróneamente que las estrategias de arbitraje estadístico son, por un lado, estrategias mágicas sin riesgo alguno y, por otro, prácticamente imposibles de aplicar para el inversor minorista medio. Este malentendido se debe a las leyendas que se han creado en torno a los resultados asombrosos obtenidos por algunos grandes operadores, a la eterna búsqueda del «Santo Grial» del trading por parte de personas sin experiencia y, en última instancia, a la falta de conocimiento sobre qué es el arbitraje estadístico.
Cuando se desarrollan adecuadamente y cuentan con el respaldo de una infraestructura adecuada para la ejecución de órdenes, pueden ofrecer un riesgo reducido y una rentabilidad constante a largo plazo. Sin embargo, en términos generales, también requieren una gestión adecuada de los riesgos, como cualquier otra actividad de trading. Una vez más, las estrategias de arbitraje estadístico no son estrategias mágicas sin riesgo alguno.
En un artículo anterior, implementamos una estrategia sencilla de trading de pares basada en la correlación que obtuvo buenos resultados en el backtest, pero que fracasó estrepitosamente cuando la pusimos a prueba durante dos semanas en una cuenta demo en tiempo real. Probablemente sea uno de los ejemplos más claros y de manual de que los backtests son útiles para evaluar la viabilidad de una estrategia, pero que, al mismo tiempo, pueden decirnos muy poco sobre cómo se comportará en condiciones reales de mercado. Y, más aún, una prueba clara de que el arbitraje estadístico no está exento de riesgos. Incluso una estrategia prometedora requiere una gestión cuidadosa de los riesgos —que no aplicamos en nuestro prototipo de muestra— y un entorno adecuado para la ejecución de órdenes, que es el tema central de este artículo.
¿Por qué los resultados de la cuenta de demostración fueron tan malos en comparación con los del backtest? ¿Cómo podríamos mejorar nuestras herramientas más allá del coeficiente de correlación —que capta el movimiento conjunto de dos activos— para disponer de un marco más sólido que permita detectar la dinámica a largo plazo en más de dos activos? ¿Cómo podemos ir más allá del simple trading de pares y ampliar la cartera a un grupo de acciones del mismo sector?
Éxito en el backtest, fracaso en tiempo real
Hace poco, mi socio y yo tuvimos una reunión informal con un operador que suele operar contratos de dólar en la bolsa brasileña B3. Dijo que le encantaría disponer de los recursos necesarios para probar «algún tipo de arbitraje estadístico con materias primas agrícolas», pero, al ser una operadora discrecional tradicional formada en análisis técnico y evolución de los precios, sin una sólida formación en matemáticas ni estadística, afirmó con rotundidad que el arbitraje estadístico estaría «fuera de su alcance». Se hacía eco de esa creencia tan extendida de la que hablamos al principio, la de que el arbitraje estadístico no está al alcance del inversor particular.
En resumen, y yendo directamente al grano, lo convertimos en un reto entre amigos: ¿y si dedicáramos nuestro tiempo libre a desarrollar un marco de arbitraje estadístico automatizado con fines de aprendizaje? Pero, ¿cómo lograrlo si ninguno de nosotros es un gran estadístico, matemático o posee la capacidad de cálculo necesaria para vencer a los grandes? Hicimos lo que suelen hacer los estudiantes: comenzamos a realizar una investigación académica para comprender el problema, las alternativas ya existentes en el mercado, estudiando los éxitos, pero también los fracasos.
Nuestros primeros pasos dieron como resultado un artículo que publiqué aquí recientemente sobre el matemático y gestor de fondos de cobertura Jim Simons y su legendario fondo Medallion. En ese artículo, utilizamos un EA minimalista para ilustrar el arbitraje estadístico entre XAUEUR y XAUUSD.
El gráfico que aparece a continuación muestra los resultados de nuestra prueba retrospectiva, publicados anteriormente aquí.
Figura 1: Resultados del backtest del EA de trading por pares (artículo anterior).
Luego, para probar su viabilidad en el mundo real, dejamos ese EA básico funcionando en una cuenta demo con comisiones de spread cero durante dos semanas. Los resultados fueron catastróficos.

Figura 2: Historial de informes de dos semanas en la cuenta demo para el EA de trading de pares.
Causas probables
Cuando buscamos las razones del fracaso de una estrategia de trading, solemos tener la tentación de empezar por "ajustar los parámetros". Pero intentemos un enfoque más sistemático: investigar las causas.
¿Dos semanas malas consecutivas?
¿Quizás se trató simplemente de dos semanas malas consecutivas en el mercado? Incluso en las pruebas retrospectivas, tuvimos algunos periodos de resultado cercano a cero e incluso algunos períodos de pérdidas mínimas, ¿no es así?
Sí, pero incluso en nuestras peores semanas, nunca habíamos tenido tantas pérdidas seguidas. En esta prueba en tiempo real, mientras que nuestro máximo de victorias consecutivas fue de solo cuatro, ¡nuestro máximo de derrotas consecutivas ascendió a cuarenta y dos! Si bien nuestro promedio de victorias consecutivas fue de solo una, nuestro promedio de derrotas consecutivas fue de cinco. En resumen, ¡estábamos perdiendo casi todas las operaciones!
Figura 3: Secuencia de pérdidas del EA de trading de pares en una cuenta demo.
Obviamente, algo fallaba gravemente, algo muy diferente a nuestro entorno de pruebas retrospectivas. Además, dado que el trading de pares es neutral al mercado por definición, es decir, no está vinculado a la rentabilidad de los valores.(El trading de pares, como la forma más simple de arbitraje estadístico, es generalmente, típicamente, neutral al mercado. Pero nuestra investigación académica demuestra que, en un sentido amplio, esto no siempre es así. Existen situaciones en las que el arbitraje estadístico puede conllevar riesgos de mercado o de sector, dependiendo del método de cobertura elegido. Abordaremos este tema cuando trabajemos en el módulo de gestión de riesgos/dinero de nuestro marco de trabajo. Por ahora, podemos considerar que nuestro sencillo sistema de negociación de pares es neutral al mercado).
Jim Simons ganó muchísimo dinero mientras los mercados se desplomaban durante la crisis de las hipotecas subprime de 2008. El fondo Medallion no fue el único fondo de cobertura que recurrió al arbitraje estadístico. ¿Por qué ganaban millones mientras otros fondos comparables eran devastados por el colapso? Parece que había algo que hacían mejor que la competencia. A pesar del carácter sumamente secreto de sus operaciones, dejaron algunas pistas valiosas que hoy en día resultan obvias, pero que con frecuencia pasan desapercibidas para los inversores minoristas. El equipo de Medallion se guiaba, y aún se guía, por enormes cantidades de datos de alta calidad. Además, invirtieron en la mejor capacidad de procesamiento y velocidad de enrutamiento de órdenes.
Así que, probablemente una mala semana no sea la causa. Resulta que, si bien el arbitraje estadístico es neutral en cuanto al mercado, al mismo tiempo es muy sensible a los datos y al momento en que se producen.
¿Nuevo entorno?
¿Quizás se debió al cambio de corredor de bolsa, con un spread diferente? Para las operaciones con pares en plazos muy cortos, con miles de entradas por semana, el spread entre el precio de compra y el de venta podría convertirse fácilmente en un problema. Como ya sabrá, los servicios que ofrecen los brókeres de divisas suelen pagarse mediante un diferencial entre la oferta y la demanda y/o una comisión porcentual calculada sobre el volumen negociado.
Pero podemos descartar la diferencia. Para la prueba retrospectiva, utilicé intencionadamente una cuenta con un spread alto, con un promedio de ~170 pips para el oro y ~50 pips para el XAUEUR. Para la cuenta demo, cambié a una cuenta con spread reducido y comisiones que cobran un promedio de ~20 pips para XAUUSD y ~10 pips para XAUEUR.
Dado que teníamos un diferencial de compra/venta más bajo en la cuenta demo, es probable que el spread no sea el culpable en este caso.
¿Deslizamiento?
El arbitraje suele ser sensible al momento oportuno, y nuestra estrategia de negociación de pares es muy sensible al momento oportuno. Veamos qué dice el acuerdo de política de ejecución de nuestro bróker de CFD sobre el momento de la orden.
El último que me viene a la mente es el sospechoso habitual: el deslizamiento. Estábamos enviando órdenes instantáneas al precio de mercado, y otro participante estaba aprovechando antes que nosotros esa diferencia de precios. Si este es el caso, aquí no hay nada malo. Este tipo de anticipación de órdenes (front-running) es legítimo. De hecho, esa es la clave del arbitraje. Simplemente significa que alguien tiene más y/o mejores recursos: mejor hardware, servidores ubicados cerca del intermediario o, simplemente, un mejor software optimizado para un alto rendimiento, mientras que nosotros estamos usando un prototipo. Probablemente, estábamos perdiendo la carrera por varios jugadores, cada uno con algunas de estas ventajas, y eventualmente algunos jugadores con todas las ventajas. Es decir, algunos grandes competidores estaban humillando nuestro humilde prototipo.
En la Guía del usuario de MetaTrader 5, vemos que podemos simular retrasos en los backtests.
“El probador de estrategias permitió emular los retrasos de la red durante una operación de Asesor Experto con el fin de proporcionar condiciones lo más cercanas posible a la realidad para las pruebas. En el probador de estrategias, se introduce un cierto retardo entre la realización de una solicitud de compraventa y su ejecución. Desde el momento en que se envía la solicitud hasta su ejecución, el precio puede variar. Esto permite a los usuarios evaluar cómo la velocidad de procesamiento de las operaciones afecta a los resultados de las mismas.
En el modo de ejecución instantánea, los usuarios también pueden comprobar la respuesta del EA a una recotización del servidor de operaciones. Si la diferencia entre el precio solicitado y el precio de ejecución supera el valor de desviación especificado en la orden, el EA recibe una nueva cotización."
Lo hicimos configurando un retardo aleatorio…
Figura 4: Configuración del probador de Metatrader 5 con retardo simulado.
… ¡y BINGO! Aunque el deslizamiento no sea el único culpable, nuestro sospechoso habitual parece ser el principal en este caso.

Figura 5: Gráfico resultante tras la ejecución con retardo simulado.
Podríamos intentar establecer la desviación máxima permitida en nuestro código, pero esta restricción socavaría por completo nuestra estrategia. Para que sea rentable, necesitamos captar el precio adecuado casi instantáneamente. Además, normalmente, la opción de establecer la desviación máxima solo está disponible en cuentas profesionales, lo que nos alejaría de nuestro objetivo principal de desarrollar un marco de arbitraje estadístico para el inversor minorista medio.
El problema principal: la ejecución
El arbitraje puede utilizarse para ganar dinero comprando y vendiendo cualquier cosa, desde tostadoras hasta tokens, desde materias primas hasta divisas, y todo lo demás. Podemos imaginar que todo comenzó cuando la gente empezó a intercambiar bienes, incluso antes del uso del dinero en el sentido en que lo entendemos hoy en día. El concepto es simple: si esa hipotética tostadora tiene un precio demasiado bajo en el mercado A, tal vez podamos comprarla allí y venderla en el mercado B, donde se vende al "precio correcto". Si la diferencia de precio es suficiente para cubrir los costes de la transacción, podemos obtener beneficios con un riesgo mínimo.
El problema es que obtener beneficios con un riesgo mínimo atrae a la gente. Cuanto mayor sea la relación riesgo-beneficio, mayor será la multitud. Cuando hablamos de arbitraje en los mercados financieros que involucran materias primas y divisas, las ganancias pueden ser enormes, y la cantidad de personas que intentan comprar el activo infravalorado también será enorme, con cada participante luchando por pujar antes que sus competidores. La necesidad de ser el primero en pujar, y de que se ejecutara el pedido, condujo finalmente al desarrollo de la infraestructura, los métodos y la práctica del comercio de alta frecuencia (HFT, por sus siglas en inglés). Como probablemente ya sabes, el trading de alta frecuencia no es tan fácil ni tan barato.
El principal problema aquí es que el inversor minorista medio no tiene los recursos necesarios para superar en velocidad a los grandes operadores. Aunque hagamos caso omiso de la falta de un equipo altamente cualificado de estadísticos y matemáticos que cuente con una gran potencia de cálculo para desarrollar los modelos, hoy en día seguimos necesitando, como mínimo, servicios de colocación (colocation), código de alto rendimiento y un intermediario capaz de enviar o enrutar la orden en unos pocos milisegundos…
Los resultados de las pruebas retrospectivas demostraron la viabilidad teórica de la estrategia en un contexto de ejecución de órdenes ideal. Es decir, nuestras órdenes se ejecutaban de inmediato para cualquier medida práctica. Sin retrasos en la red, sin errores en el servidor del intermediario.
Es fácil ver que no está dirigido al inversor minorista medio. Como modestos inversores minoristas, si queremos beneficiarnos del arbitraje en los mercados financieros, tendremos que encontrar alternativas que vayan más allá de nuestro prototipo ingenuo de negociación de pares. Necesitamos explorar y encontrar patrones y anomalías que no estén tan ligados a la velocidad.
De la correlación a la cointegración
Anteriormente, presentamos el mercado financiero tal como lo describió Jim Simons, es decir, como un enigma en constante cambio. Un enigma que exige una adaptación permanente por parte de los operadores para maximizar las ganancias y minimizar las pérdidas. Creamos una "cartera" mínima de dos activos, XAUUSD y XAUEUR, y elegimos la desviación de la media de su cotización frente al dólar estadounidense como la relación estadística a analizar. El ensanchamiento del spread y su posterior reversión a la media fueron nuestras señales de entrada y salida, respectivamente.
En este punto, nuestra "cartera" de arbitraje estadístico es muy limitada, al igual que nuestras relaciones estadísticas. Además, dado que la velocidad está fuera de nuestro control, podemos considerarla, por ahora, como una optimización prematura. En primer lugar, debemos mejorar nuestras capacidades antes de dedicar nuestro tiempo a lo que está fuera de nuestro control. Necesitamos ampliar nuestra cartera de productos. Necesitamos incorporar más relaciones estadísticas a nuestro arsenal de herramientas de selección y más medidas estadísticas que sirvan como señal de entrada.
El criterio que utilizamos para seleccionar esos pares fue la correlación. Limitamos nuestra búsqueda a los principales pares de divisas Forex y seleccionamos el par con mayor correlación en un intervalo de tiempo determinado. Ahora incorporaremos la cointegración al proceso de selección para construir la cartera.
Según los estadísticos,
"La correlación solo capta las relaciones a corto plazo, mientras que la cointegración capta las relaciones de equilibrio a largo plazo." Dos variables pueden estar altamente correlacionadas pero no cointegradas, y viceversa." [Alexander, 2001]
El coeficiente de correlación nos indica hasta qué punto los precios de XAUUSD y XAUEUR tienden a moverse en la misma dirección simultáneamente. Una prueba de cointegración detectará una equivalencia entre los activos que sea válida a largo plazo. Además, al utilizar el coeficiente de correlación, estamos limitados a dos activos, pero al realizar pruebas de cointegración, esta limitación no se aplica, lo que significa que podemos probar varios candidatos a cartera al mismo tiempo. Esto hace que una prueba de cointegración sea ideal para nuestros propósitos en este caso.
Como operadores o desarrolladores de EA, no necesitamos conocer las matemáticas que hay detrás de las pruebas de cointegración. Dado que utilizaremos la integración de Python para Metatrader 5 para nuestro análisis de datos y selección de cartera, podemos usar las funciones de Python predefinidas de bibliotecas especializadas. Además, los asistentes de IA que consulté al escribir este artículo, incluidos los GPT integrados en Metatrader 5, pueden ayudarte y ahorrarte tiempo al desarrollar scripts sencillos para este fin.
Como operadores, necesitamos saber qué significa la cointegración y cómo podemos utilizarla para identificar oportunidades, especialmente en el mercado de valores, donde se sabe que la cointegración es más útil y efectiva que con las divisas. Como desarrolladores de EA, necesitamos saber cómo utilizar las mejores bibliotecas disponibles y cómo interpretar los resultados, para poder iterar y mejorar. Al utilizar bibliotecas de alta calidad —y el ecosistema de Python destaca por sus bibliotecas de alta calidad para matemáticas, estadística y aprendizaje automático— ahorramos tiempo, evitamos errores y posiblemente obtenemos un mejor rendimiento.
Si quieres profundizar en el tema, te recomiendo que consultes el libro citado anteriormente, de la autora Carol Alexander, más concretamente el capítulo 12, que está dedicado íntegramente a la cointegración. En ese libro, la Sra. Alexander se basa en la historia del uso de la cointegración en las finanzas para analizar su aplicabilidad en diferentes mercados, incluido el de divisas (Forex). Su libro de texto es una referencia muy conocida en el campo.
Veamos cómo podemos aplicar dos de las pruebas de cointegración más recomendadas, la prueba de Engle-Granger y la prueba de Johansen, en diferentes clases de activos y cómo interpretar sus resultados.
La prueba de cointegración de Engle-Granger
La prueba de cointegración de Engle-Granger se puede utilizar para evaluar el nivel de cointegración entre dos activos. Su propósito es responder a la pregunta: ¿Existe una relación de equilibrio a largo plazo entre estos dos activos? En lo que nos concierne como operadores y desarrolladores de EA, esta prueba nos indicará si el spread entre los dos activos tiende a regresar a su media. Pero la respuesta no es binaria, sí o no. Al igual que la prueba de correlación de Pearson que vimos anteriormente, nos devolverá un valor relativo (valor p) que deberá compararse con los resultados de otros pares para indicarnos cuál de ellos está más integrado.
El valor p es la probabilidad de observar los datos si NO hubiera cointegración. Es decir, cuanto menor sea el valor p, mejor. Existen umbrales bien conocidos. Un valor p < 0,05 se suele considerar una señal moderada de cointegración. Un valor p < 0,01 es una señal fuerte, y cualquier valor p > 0,05 es una señal de que no hay cointegración. Como siempre en el trading, conviene ajustar estos umbrales a su caso de uso específico.
El código Python para realizar ambas pruebas de cointegración utiliza la conocida biblioteca statsmodels.
“Un módulo de Python que proporciona clases y funciones para la estimación de muchos modelos estadísticos diferentes, así como para realizar pruebas estadísticas y explorar datos estadí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']
Buscamos el par de divisas más cointegrado entre los principales pares de divisas. No esperamos encontrar una tasa de cointegración elevada. Esto solo servirá como ilustración de cómo funciona la prueba 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 las variables utc_from y utc_to para solicitar cotizaciones de símbolos con mt5.copy_rates_range durante 600 días de negociación. Un año tiene aproximadamente 250 días hábiles.
# 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']
En este bucle for, solicitamos las cotizaciones de cierre para cada símbolo, las convertimos a un DataFrame de Pandas y establecemos la matriz 'time' como nuestro índice de datos tabulares.
Tras concatenar los marcos de datos y eliminar las filas con valores faltantes que podrían distorsionar nuestros resultados, ejecutamos la prueba de cointegración.
# 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)
Una vez identificados el par con mayor cointegración, podremos decidir si los incorporamos a nuestra cartera de operaciones con pares. En el gráfico que aparece a continuación, podemos ver claramente que el spread está volviendo a la media, pero la pregunta es: ¿merece la pena realizar la operación?

Figura 6: Gráfico del spread cointegrado entre USDCAD y NZDUSD con la media y dos desviaciones estándar.
La línea negra discontinua indica la media del spread. Este es el nivel de equilibrio a largo plazo. Las dos líneas rojas discontinuas indican las dos bandas de desviación estándar (DE). Estos parámetros definen umbrales para identificar posibles señales de negociación cuando el spread es inusualmente alto o bajo, por lo que el número de desviaciones estándar es un factor importante a considerar para su optimización.
El coeficiente de cobertura
Obsérvese el valor de -1,54 utilizado en el cálculo del spread a largo plazo.
Spread = USDCAD - (-1.54) * NZDUSD
o
USDCAD + 1.54 * NZDUSD
Este valor es el índice de cobertura. Esta es la relación óptima, estimada estadísticamente mediante regresión lineal, que alinea el USDCAD y el NZDUSD de manera que su diferencial sea estacionario, es decir, que oscile alrededor de la media constante, lo cual es clave para las estrategias de reversión a la media. Al utilizar este índice de cobertura, sabemos que estamos comparando precios en la misma escala, corrigiendo su volatilidad y relación direccional.
El diferencial refleja las diferencias relativas en los precios. En la jerga del trading de pares, diríamos que cuando el spread está por encima de +2 STD, es históricamente alto y podemos considerar venderlo en corto; por el contrario, cuando el spread está por debajo de -2 STD, es históricamente bajo y podemos considerar comprarlo.
Para construir un par de divisas neutral al mercado para operar basándonos en estos datos, por cada unidad de USDCAD que compraríamos, venderíamos simultáneamente en corto 1,54 unidades de NZDUSD (o viceversa, dependiendo de la dirección de la señal). Luego, cuando el spread vuelve a acercarse a la media, salimos de la operación, cerrando ambas posiciones.
Puedes utilizar este mismo código con símbolos de cualquier mercado o clase de activo. Para usarlo tal cual, simplemente reemplace los nombres de los símbolos que se encuentran en la parte superior.
La prueba de cointegración de Johansen
La prueba de cointegración de Johansen pretende responder a la misma pregunta que la prueba de Engle-Granger: ¿comparten estos activos una relación de equilibrio a largo plazo? En la práctica, y para nuestros propósitos aquí, la principal diferencia es que la prueba de Johansen puede evaluar más de dos activos al mismo tiempo y nos dirá si estos N activos comparten un diferencial común que revierte a la media. Podemos considerarlo una herramienta poderosa para evaluar la posible cointegración en un grupo de acciones del mismo sector, como acciones petroleras, acciones tecnológicas, etc. Nos permite encontrar tres, cuatro o más acciones (o cualquier otro activo) que compartan una relación de equilibrio común a largo plazo.
Comprueba si existen combinaciones de las variables que sean estacionarias (que reviertan a la media), incluso si las series originales no lo son, y estima cuántas relaciones de cointegración existen entre las variables. Este número se denomina rango de cointegración.
La prueba produce dos estadísticas principales: la estadística de traza y el valor crítico.
Se obtiene una estadística de traza para cada rango posible (0, 1, 2, ..., N−1 donde N es el número de series).Un valor estadístico de traza mayor sugiere una evidencia más sólida de cointegración. El valor crítico es el umbral con el que se compara la estadística de traza. Depende de:
- El número de variables que se están evaluando
- La longitud de la muestra
- El nivel de confianza (90%, 95% o 99%)
En este caso concreto, podemos pensar en el número de variables como el número de símbolos que se están probando.
# 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'}")
Podemos ver los resultados para los demás niveles de confianza configurando:
johansen_result.cvt[i, 0] for 90%
johansen_result.cvt[i, 2] for 99%
Para un nivel de confianza del 95% (valor crítico del 5%), estos son nuestros resultados para los principales pares de divisas en el mismo período y marco temporal D1 que analizamos con 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
Tal y como se esperaba, no se observa una cointegración significativa entre las siete divisas principales del mercado de divisas.
Sin embargo, si analizamos las dos conocidas acciones de Google y Nvidia en el intervalo de tiempo H1 (véase más abajo), obtenemos un resultado de cointegración significativo para el mismo periodo e intervalo de confianza.
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 del spread cointegrado entre NVDA y GOOGL con la media y dos desviaciones estándar.
Bien, tenemos una cointegración significativa para GOOGL y NVDA según Johansen, pero un valor p de Engle-Granger superior a 0,05, lo que indica que no hay cointegración. ¿A qué se deben estos resultados contradictorios? ¿Qué quiere decir esto?
Ejecutemos el mismo script, pero invirtiendo el orden de los símbolos, y verás una diferencia interesante entre las pruebas de Engle-Granger y 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 del spread cointegrado entre GOOGL y NVDA con la media y dos desviaciones estándar.
Como puede observarse, la prueba de Engle-Granger es sensible al orden, mientras que el Johansen no se vio afectado por el cambio. Si profundizas en las sutilezas de la prueba de Johansen, descubrirás que puede mostrar cierta sensibilidad al orden en algunos casos, en particular para muestras cortas o si estamos al límite de la significación estadística. Pero, por regla general, teóricamente es insensible al orden. Si observa que los resultados son inconsistentes después de cambiar el orden, intente agregar más datos a su muestra como primera medida. Por eso, sustituimos el marco temporal D1 (diario) por un marco temporal H1 (horario), para proporcionar una muestra mayor para la prueba.
Sabiendo que la prueba de Engle-Granger es sensible al orden de las variables, se sugiere que siempre se deben probar ambas direcciones y evaluar los resultados mediante la inspección visual de los gráficos para ver si la dispersión está volviendo a la media.
Probar la estacionariedad del spread
La diferencia es una serie temporal, y para asegurarnos de que vuelve a la media, debemos comprobar su estacionariedad, es decir, debemos comprobar que su media, varianza y covarianza no cambien con el tiempo. Es más fácil modelar y pronosticar utilizando técnicas como los modelos ARIMA si la serie temporal es estacionaria.
La misma biblioteca de Python que hemos estado utilizando para probar la cointegración tiene funciones predefinidas para probar la estacionariedad, la prueba de Dickey-Fuller aumentada ("ADF") y la prueba de Kwiatkowski-Phillips-Schmidt-Shin ("KPSS").
from statsmodels.tsa.stattools import adfuller (...) adf_result = adfuller(spread)
Prueba ADF sobre el spread:
ADF Statistic : -3.0946
p-value : 0.0270
Critical Values:
1%: -3.4315
5%: -2.8620
10%: -2.5670
✅ El spread es estacionaria (se rechaza la hipótesis nula).
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)
Prueba KPSS sobre el 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
❌ El diferencial NO es estacionario (se rechaza la hipótesis nula de estacionariedad).
Parece que volvemos a tener resultados contradictorios. ADF afirma que nuestro spread es estacionario, pero KPSS no está de acuerdo. Que no cunda el pánico. Los responsables del mantenimiento de statsmodels han tenido la amabilidad de ofrecernos una explicación clara sobre cómo interpretar ambas pruebas en cualquier combinación posible de resultados. A continuación voy a citar la documentación de statsmodels, porque creo que es difícil ser más conciso y claro.
Caso 1: Ambas pruebas concluyen que la serie no es estacionaria - La serie no es estacionaria.
Caso 2: Ambas pruebas concluyen que la serie es estacionaria - La serie es estacionaria.
Caso 3: KPSS indica estacionariedad y ADF indica no estacionariedad: la serie es estacionaria en tendencia. Es necesario eliminar la tendencia para que la serie sea estrictamente estacionaria. Se comprueba la estacionariedad de la serie a la que se le ha eliminado la tendencia.
Caso 4: KPSS indica no estacionariedad y ADF indica estacionariedad: la serie es estacionaria en diferencias. La diferenciación se utiliza para hacer que las series sean estacionarias. Se comprueba la estacionariedad de la serie diferenciada.
Ahora nos informan que nuestro spread es estacionario en diferencias y no estrictamente estacionario. ¿Qué quiere decir esto? Esto significa que esta serie temporal puede presentar desviaciones, pero los cambios son estables. Se puede transformar en una serie temporal estrictamente estacionaria simplemente tomando las diferencias entre valores consecutivos. Como era de esperar, esta técnica se llama diferenciación. Elimina las tendencias y la estacionalidad, y prepara nuestras series temporales para un análisis y una previsión más eficaces.
¿Necesitamos convertir nuestro spread en una serie estrictamente estacionaria? Muchos modelos de series temporales requieren que los datos sean estacionarios, pero este no es nuestro caso. En nuestra implementación de cointegración no utilizaremos modelos de pronóstico.
No realizaremos ningún tipo de pronóstico, pero quizás no esté de más saber que el análisis de cointegración no termina con las pruebas de cointegración.
Conclusión
En este artículo, dimos un paso más en la construcción de nuestro marco de arbitraje estadístico para el humilde inversor minorista. Hemos añadido dos herramientas más a nuestro conjunto de herramientas estadísticas para la creación de carteras: las pruebas de cointegración de Engle-Granger y de Johansen. Vimos qué podíamos esperar de cada uno de ellos y cómo realizar la interpretación más básica de sus resultados. Además, vimos cómo comprobar la estacionariedad del spread utilizando la prueba de Dickey-Fuller aumentada ("ADF") y la prueba de Kwiatkowski-Phillips-Schmidt-Shin ("KPSS").
Este fue el primero de dos artículos que presentaban las herramientas básicas. El siguiente artículo complementará esta introducción con una implementación, pruebas retrospectivas y optimizaciones para un par de símbolos de divisas cointegrados y para un grupo de símbolos bursátiles cointegrados del mismo sector.
Me atrevo a decir que el contenido más relevante aquí no es la presentación de las herramientas, las funciones, ni siquiera el código de ejemplo. El dato más relevante aquí es que, si eres un inversor minorista, no una persona con inclinación por las matemáticas o la estadística, un inversor que no es desarrollador, la habilidad más valiosa que posees al desarrollar tu estrategia de arbitraje estadístico es la comprensión del activo o grupo de activos con los que trabajas.
Tu conocimiento sobre los mercados no tiene precio, porque profesionales altamente cualificados ya han realizado el arduo trabajo en matemáticas y estadística, y este trabajo está disponible de forma gratuita en muchas bibliotecas de código abierto, incluidas las bibliotecas MQL5 disponibles en la plataforma Metatrader 5, junto con el valioso Asistente de IA para ayudarte con la correcta implementación.
Lección aprendida: si dispone de recursos limitados, evite las estrategias que dependen de la velocidad e intente explorar los datos de forma creativa, aprovechando su conocimiento del mercado.
Referencias
- 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.
| Archivo adjunto | Descripción |
|---|---|
| coint.ipynb | Este archivo es un cuaderno de Jupyter simplificado que contiene código en Python para ejecutar una única prueba de cointegración de Engle-Granger para un par de instrumentos financieros. |
| coint_googl_nvda.ipynb | Este archivo es un cuaderno de Jupyter ampliado que contiene código en Python para ejecutar tanto las pruebas de cointegración de Engle-Granger como las de Johansen sobre un número (teóricamente) ilimitado de instrumentos financieros, junto con la prueba de estacionariedad del spread. |
| helper_quotes_to_db.ipynb | Este archivo sirve para guardar las cotizaciones de valores descargadas en una base de datos sqlite3. Además, es un cuaderno de Jupyter con código en Python. Su objetivo es evitar descargas innecesarias y permitir trabajar sin conexión. |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/18702
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Red neuronal cuántica en MQL5 (Parte II): Entrenamos una red neuronal con retropropagación del error utilizando matrices de Márkov de ALGLIB
Negociamos con opciones sin opciones (Parte 2): Aplicación en el trading real
Particularidades del trabajo con números del tipo double en MQL4
Cómo creamos la plataforma de trading más potente con aprendizaje automático: crónica del desarrollo de MQL y MetaTrader según los archivos, foros y lanzamientos
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
¡Qué artículo tan interesante! He trabajado antes en proyectos similares y también he tenido problemas con la ejecución en tiempo real. :)
Pero el artículo está bien escrito. Los que se inician en este tema podrán seguirlo fácilmente.