---
title: "Diseño de una Estrategia con Python: Trading de Pares"
date: 2022-07-07T11:45:21Z
modified: 2024-07-05T16:10:47Z
permalink: "https://www.x-trader.net/diseno-de-una-estrategia-con-python-trading-de-pares/"
type: post
status: publish
excerpt: La revista Hispatrading comparte con nosotros este artículo de Gerard Sánchez en el que nos explica cómo construir una estrategia de pares con Python.
wpid: 10202
categories:
  - Sistemas de Trading
tags:
  - Python para Trading
  - Trading con Spreads
featured_image: "https://www.x-trader.net/wp-content/uploads/2014/09/intro_codigo-programacion.jpg"
featured_image_alt: intro_codigo-programacion
author: X-Trader
---

**_El trading de pares es una estrategia en la que operamos 2 activos que durante un periodo tienen un comportamiento similar por estar correlacionados. Buscamos arbitrar diferencias abriendo posiciones largas o compradoras sobre un activo y vendedoras o cortas en el otro, esperando una reversión a la media. ¿Cómo podemos crear una estrategia en base a esto?_**

Lo deseable es que el comportamiento del activo sobre el que estamos largos o comprados tenga un movimiento relativamente más alcista que el otro par y al contrario con el par en el que estamos vendidos.

En este artículo vamos a tratar de automatizar señales de compra/venta en una [estrategia de pares](https://www.x-trader.net/tag/trading-con-spreads/) para el universo de compañías del SP500 basándonos en el coeficiente de correlación y normalizando series de retornos con el ratio z-score.

En primer lugar vamos a necesitar una serie de librerías que nos ayudarán con el proceso de descarga y modelización de los datos, son las siguientes:

![Codigo Python 01](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-01.png)

Procedemos a descargar los precios de cierre para todas las compañías del SP500 para un periodo, calculando los retornos o rentabilidades de cada uno de ellos y calculando el coeficiente de correlación sobre los mismos.

Esto nos generará una matriz en la que se determina el coeficiente de correlación que comparte cada activo con el resto de compañías del índice para un periodo.

![Codigo Python 02](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-02.png)

![Correlacion entre Pares](https://www.x-trader.net/wp-content/uploads/2022/07/Correlacion-Pares.png)

Deshacemos la matriz y ordenamos todos los pares generados en orden descendiente por correlación.

![Codigo Python 03](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-03.png)

![Pares Generados](https://www.x-trader.net/wp-content/uploads/2022/07/Pares-Generados.png)

En total se nos han generado 121771 parejas posibles, de las cuales vamos a escoger únicamente las que tengan una correlación mayor a 0.8, suficiente como para saber que estas acciones han tenido un comportamiento similar en el pasado.

En total 422 pares, 844 activos.

A continuación creamos un nuevo dataframe llamado “df1” con los retornos para los 848 activos ordenados por correlación filtrada hasta un mínimo de 0.8.

![Codigo Python 04](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-04.png)

Calculamos los retornos acumulativos de cada activo partiendo de 1, manteniendo el orden de las parejas.

![Codigo Python 05](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-05.png)

Normalizamos la serie de retornos por pares (el primero menos el segundo, el tercero menos el cuarto…) y lo juntamos todo en un nuevo dataframe llamado “resultados”.

Con esto podremos ver si hay diferencias entre los retornos de un activo con el otro de forma agregada.

![Codigo Python 06](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-06.png)

Generamos el nuevo nombre de cada columna, que será la unión entre el nombre de los tickers de los pares.

![Codigo Python 07](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-07.png)

![Cambio Columnas Retornos](https://www.x-trader.net/wp-content/uploads/2022/07/Cambio-Columnas-Retornos.png)

Calculamos el ratio estadístico z-score para saber qué tan distantes son los resultados con respecto a sus medias basándonos en desviaciones estándar.

![Codigo Python 08](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-08.png)

Dibujamos las series normalizadas de los primeros 8 pares y los últimos 8 para tener una idea del comportamiento que han tenido, tanto para los activos con correlación más cercana a 1 como a 0.8, también dibujamos 2 bandas, una en 2 y otra en -2 para determinar posibles puntos de compra y/o de venta suponiendo una reversión a la media.

![Codigo Python 09](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-09.png)

![Series Normalizadas 1](https://www.x-trader.net/wp-content/uploads/2022/07/Series-Normalizadas.png)

![Series Normalizadas 2](https://www.x-trader.net/wp-content/uploads/2022/07/Series-Normalizadas-2.png)

A partir de aquí, decidiríamos qué tipo de operativa vamos a ejecutar de forma sistemática.

Un ejemplo podría ser el de ponernos largos del par (comprar la primera acción y vender la segunda) cuando la normalización cae a -2 y al revés cuando la normalización sube a 2, cerrando la operación cuando la normalización toque -0.5 o 0.5 en el caso contrario.

Hay muchas posibilidades que se pueden backtestear.

Vamos a señalizar todos los cruces en que la normalización toca esos puntos en dos dataframes distintos: signalc y signalv.

![Codigo Python 10](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-10.png)

Normalizamos y reordenamos nuestros precios de cierre por columnas de activos aparejados por correlación, igualando el número de filas al dataframe de las señales, para poder graficar los activos con esas señales superpuestas.

![Codigo Python 11](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-11.png)

Ahora mostramos el 5º par a modo de ejemplo (RF-KEY), dibujando encima de los precios de cierre esas señales de compra/venta cuando la normalización de las series tocan 2 y -2.

![Codigo Python 12](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-12.png)

![Estrategia de Pares](https://www.x-trader.net/wp-content/uploads/2022/07/Estrategia-de-Pares-1024x655.png)

Podemos realizar otro tipo de análisis de las series utilizando describe(), a continuación se muestra un ejemplo ordenando por valores mínimos de menor a mayor:

![Codigo Python 13](https://www.x-trader.net/wp-content/uploads/2022/07/Codigo-Python-13.png)

Todo este ejemplo nos lleva a poder partir de la base para realizar la construcción de nuestra estrategia de pares, decidiendo qué señales de compra/venta utilizaremos para operar y con qué gestión de capital.

P.D.: Pongo todo el código para el que quiera utilizarlo directamente:


```
import yfinance as yf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yahoo_fin.stock_info as si

# DESCARGA DEL UNIVERSO DE ACTIVOS + CÁLCULO CORRELACIÓN
tickers = si.tickers_sp500()
data = yf.download(tickers, start='2018-01-04', progress=False)['Close']
returns = data.pct_change()[1:].dropna(axis=1)
corr = returns.corr(method='pearson')

# NOS QUEDAMOS CON LOS ACTIVOS >0.8 CORRELACIÓN
upper = corr.where(np.triu(np.ones(corr.shape), k=1).astype(np.bool8)).unstack().dropna().sort_values(ascending=False)
upper = upper[upper>0.8]
upper = list(upper.index)
tickercurados = list([x for ticker in upper for x in ticker])
tickercurados
len(tickercurados)
df1 = returns.reindex(columns=tickercurados)
df1.columns

# CALCULO LOS RETORNOS ACUMULATIVOS
cr = (1+df1).cumprod()

# RESTO LOS RETORNOS ACUMULATIVOS (EL PRIMERO MENOS EL SEGUNDO)
resultados = pd.DataFrame()
for i in range(0,len(df1.columns),2):
diferencia = cr.iloc[:,i] - cr.iloc[:,i+1]
resultados = pd.concat([resultados,diferencia],axis=1)

# GENERO EL NUEVO NOMBRE TICKER-TICKER DE CADA COLUMNA CON LOS RETORNOS RESTADOS.
l = []
for i in range(0,len(tickercurados),2):
tickers = "-".join(tickercurados[i:i+2])
l.append(tickers)
resultados.columns = l

# NORMALIZAMOS LA SERIE: ((X-X.MEAN())/X.STD()
z_score=pd.DataFrame()
for i in range(len(resultados.columns)):
serie = (resultados.iloc[:,i] - resultados.iloc[:,i].mean())/resultados.iloc[:,i].std()
z_score = pd.concat([z_score,serie],axis=1)

# DIBUJO DE SERIES NORMALIZADAS (Z-SCORE)
t = "Series normalizadas de pares >0.8"
z_score.iloc[:,:8].plot(figsize=(20,8),title=t)
[plt.axhline(y=i, linestyle='--', lw=2, color='r') for i in [-2,2]]
plt.show()

# SEÑALIZAMOS CRUCES EXACTOS PARA COMPRA/VENTA
for i in range(len(z_score)):
signalv = pd.DataFrame(np.where(z_score>2,1,0), index= z_score.index, columns=z_score.columns)
signalc = pd.DataFrame(np.where(z_score<-2,1,0), index= z_score.index, columns=z_score.columns)
signalv = signalv.diff()
signalc = signalc.diff()
data = data.reindex(columns=tickercurados)[1:]
data = data/data.iloc[0]

# DESCRIPCIÓN DEL DATAFRAME
z_score.describe().T.head(15).sort_values(by='min', ascending=True).drop(['count'],axis=1)

# DIBUJAMOS UN EJEMPLO DE PARES (EL 5º PAR) CON COMPRAS/VENTAS PARA EL PERIODO:
fig = plt.figure(figsize=(20,10))
ax1 = fig.add_subplot(111,title="Estrategia de Pares" ,ylabel='Pares',xlabel="Fechas")
ax1.plot(data.iloc[:,8][signalc.iloc[:,8] == -1],'v', markersize=12, color='g')
ax1.plot(data.iloc[:,8][signalv.iloc[:,8] == -1],'^', markersize=12, color='r')
ax1.plot(data.iloc[:,9][signalc.iloc[:,9] == -1],'v', markersize=12, color='g')
ax1.plot(data.iloc[:,9][signalv.iloc[:,9] == -1],'^', markersize=12, color='r')
ax1.plot(data.iloc[:,[8,9]], label=data.iloc[:,[8,9]].columns, lw=2)
ax1.legend()
ax1.grid()
plt.show()
```

**_Artículo publicado en el número 51 de la revista Hispatrading. Regístrate en [www.hispatrading.com](https://www.hispatrading.es/) de manera completamente gratuita para acceder a más artículos como este._**

## Topics

**Categorías:** [Sistemas de Trading](https://www.x-trader.net/wp-content/uploads/wp-mfa-exports/taxonomy/category/sistemas-de-trading.md)

**Etiquetas:** [Python para Trading](https://www.x-trader.net/wp-content/uploads/wp-mfa-exports/taxonomy/post_tag/python-para-trading.md), [Trading con Spreads](https://www.x-trader.net/wp-content/uploads/wp-mfa-exports/taxonomy/post_tag/trading-con-spreads.md)