Quant500 Academy

7. Pairs trading: la diferencia, no la dirección

Nivel 5: Estrategias de Momentum, Mean-Reversion y Pairs Trading

El pairs trading lleva la idea de reversión a la media a dos activos relacionados en vez de a uno solo. En lugar de mirar si un precio se ha alejado de su propia media, se mira la diferencia (el spread) entre dos activos, y se apuesta a que esa diferencia — no el precio de cada uno por separado — vuelve a su nivel habitual.

Para que esta apuesta tenga sentido estadístico, los dos activos deben estar cointegrados: en términos sencillos, que aunque cada uno se mueva de forma más o menos aleatoria por separado, la diferencia entre ambos se mantiene estable alrededor de una media a largo plazo. En la práctica, dos empresas que venden productos sustitutivos entre sí (Coca-Cola y Pepsi, por ejemplo) suelen estar cointegradas: ambas se mueven con el ciclo del consumo y del sector, sin alejarse mucho la una de la otra de forma sostenida.

from statsmodels.tsa.stattools import coint

datos = yf.download(["KO", "PEP"], start="2020-01-01", end="2023-01-01", progress=False)
precio_ko = datos["Close"]["KO"]
precio_pep = datos["Close"]["PEP"]

estadistico, pvalor, valores_criticos = coint(precio_ko, precio_pep)

El test de cointegración plantea una hipótesis nula de que NO existe cointegración entre las dos series, frente a la hipótesis alternativa de que sí la hay. Un p-valor pequeño (habitualmente por debajo de 0.05) permite rechazar esa hipótesis nula: hay evidencia estadística de que la relación es cointegrada, y por tanto la estrategia tiene una base razonable.

Con la cointegración confirmada, la mecánica es la misma que en el módulo anterior, aplicada al spread en vez de al precio de un solo activo: cuando el spread se aleja por encima de su media (z-score > 1), te pones corto en el activo que subió relativamente más y largo en el que subió relativamente menos; cuando el spread cae por debajo (z-score < -1), la posición se invierte.

spread = precio_pep - precio_ko

def zscore(serie):
    return (serie - serie.expanding().mean()) / serie.expanding().std()

z = zscore(spread)

La ventaja de operar el spread, en vez de cada activo por separado, es que la estrategia queda neutral al mercado: si ambas acciones caen un 5% por una noticia macro que afecta a todo el sector, el spread apenas se mueve — pero ojo, eso solo es cierto si el spread está ponderado. La resta bruta de precios no lo está: con PEP a 180 y KO a 60, una caída común del 5% lleva el spread de 120 a 114. Hay que usar el ratio de cobertura que estima la propia regresión de cointegración (spread = precio_pep - beta * precio_ko), o trabajar con el ratio de precios o con log-precios — la ganancia o pérdida viene de la diferencia relativa entre ambas, no de la dirección general del mercado.

Para no limitarte a probar pares al azar, se puede automatizar la búsqueda: recorrer todas las combinaciones posibles dentro de un universo de acciones y quedarte solo con las que superan el test de cointegración.

def encontrar_pares_cointegrados(precios):
    n = precios.shape[1]
    matriz_pvalores = np.ones((n, n))
    columnas = precios.columns
    pares = []

    for i in range(n):
        for j in range(i + 1, n):
            _, pvalor, _ = coint(precios[columnas[i]], precios[columnas[j]])
            matriz_pvalores[i, j] = pvalor
            if pvalor < 0.05:
                pares.append((columnas[i], columnas[j]))

    return matriz_pvalores, pares

El bucle for j in range(i + 1, n), empezando en i + 1 y no en 0, evita dos redundancias: comparar un activo consigo mismo, y comparar dos veces el mismo par en orden inverso (probar A-B es exactamente lo mismo que probar B-A para efectos de cointegración). Es un detalle pequeño, pero en un universo de cientos de acciones, evitar comparaciones redundantes ahorra una cantidad real de cómputo.

Para ampliar esta lección

El resto de Quant500

Lo que estás aprendiendo, aplicado al índice todos los días:

La cartera del modelo, cada semana en tu correo

Cada sábado, su semana en un minuto: cómo fue, lo mejor y lo peor, y qué directivos compraron con su dinero. Y el día que el modelo se rehace, qué entra y qué sale. Es una simulación, no un consejo. Como mucho un correo a la semana, sin publicidad y sin cuenta; te das de baja con un clic. Lo hace una persona: Pol Gracia.

Cartera modelo con resultados simulados, no una recomendación personalizada. Tu correo se usa solo para esto: privacidad.

Detrás de la Academia y del resto de Quant500 hay una sola persona: Pol Gracia Serrahima.