Cómo calcular correlaciones
A la hora de ver las correlaciones entre los distintos mercados es importante darse cuenta que deben ser calculadas con los retornos diarios y no con el precio de los activos. El cálculo de correlaciones es muy tedioso si son muchos mercados ya que debe hacerse "todos con todos" y por tanto se necesita la potencia de un lenguaje de programación orientado a manejar gran volumen de datos, como es Python y en especial la librería Pandas.
Haremos lo siguiente:
1. Desde amibroker exportamos los datos a una carpeta "Quotes" de nuestro disco duro. Podemos usar el siguiente código:
--------------------------------------------------------------------------------------------------------------
/*
Export intraday and EOD data to TXT files
One file for each stock
In the first line insert the directory you want to save them to, make sure the directory exists
Select your charts to export with the "Apply to" filter in AA window
Select the timeframe period you want to save as using the AA "Settings"
Press Scan button
by Graham Kavanagh 05 Feb 2004
C:\Users\OSCAR\Documents\Quotes
*/
Plot(C,"Last",colorRed,styleBar, Null, Null, 0, 0, 2);
fh = fopen( "C:\\Users\\OSCAR\\Documents\\Quotes\\"+Name()+".csv", "w");
if( fh )
{
fputs( "Symbol,Date,Time,Open,High,Low,Close,Volume \n", fh );
y = Year();
m = Month();
d = Day();
for( i = 0; i < BarCount; i++ )
{
fputs( Name() + "," , fh );
ds = StrFormat("%02.0f%02.0f%02.0f,", y[ i ], m[ i ], d[ i ] );
fputs( ds, fh );
//ponemos un cero en el tiempo
fputs("0,",fh);
qs = StrFormat("%.4f,%.4f,%.4f,%.4f,%.0f\n", O[ i ],H[ i ],L[ i ],C[ i ],V[ i ] );
fputs( qs, fh );
}
fclose( fh );
}
Buy=Sell=0; // for scan
Filter = Status("lastbarinrange");
AddTextColumn("Exportado!", "Status");
--------------------------------------------------------------------------------------------------------------
Este es el resultado:
2. Ahora que tenemos los ficheros con los datos en la carpeta Quotes los podemos procesar en Python con el siguiente código:
--------------------------------------------------------------------------------------------------------------
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 26 19:34:39 2017
@author: OSCAR
CALCULA LA MATRIZ DE CORRELACIONES DE LOS SÍMBOLOS QUE ESTÉN EN LA CARPETA QUOTES2
"""
import os
import glob
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn
# These settings modify the way pandas prints data stored in a DataFrame.
# In particular when we use print(data_frame_reference); function - all
# column values of the frame will be printed in the same row instead of
# being automatically wrapped after 6 columns by default. This will be
# for looking at our data at the end of the program.
#pd.set_option('display.height', 1000)
pd.set_option('display.max_rows', 500)
pd.set_option('display.max_columns', 500)
pd.set_option('display.width', 1000)
# Using glob library to create a list of file names using regular expression.
datafiles= glob.glob("C:/Users/OSCAR/Documents/Quotes\*.csv")
# Create an empty python dictionary which will contain currency pairs' data.
# Keys will be currency pair names, and values- 'pandas' data frames with
# close prices read from the files.
dataframes = dict()
# In the following loop we'll read each file into a pandas data frame and
# store them in the 'dataframes' dictionary.
# for each file...
for f in datafiles:
#SACAMOS EL NOMBRE DEL FICHERO SIN LA EXTENSIÓN
pair_name = os.path.splitext(os.path.basename(f))[0]
# Using read_csv function read file into a DataFrame 'df'.
print(pair_name)
# Notice that we are reading only two columns from each file: 'date', and 'close'.
# We'll be using 'date' to index each record in data frame (think of it a a primary
# key value for the record) , and the close price will be used to calculate correlations.
df = pd.read_csv(f, sep=',', header=0, index_col=["Date"], usecols=["Date", "Close"])
# Rename 'close' column the the currency pair name pair.
# This will help us identify each pair's close price below when we join all
# data frames into a single fame.
df.columns = [pair_name]
# cagigas: he cambiado el precio por el retorno porcentual
dataframes[pair_name] = df.pct_change()
# In this section we'll join all data frames create above into a single 'final_df'
# data frame. This data frame will contain a single 'date' column, and 1 column
# for each currency pair containing that pair's close prices.
final_df = None
for k,v in dataframes.items():
if (final_df is None):
final_df = v
else:
# Panda's join operation is similar to an SQL join. In this case
# we are using a 'left' join.
#http://pandas.pydata.org/pandas-docs/stable/merging.html
final_df = final_df.join(v, how='left')
print("--------------- FINAL DATA FRAME ---------------")
print(final_df.tail(10))
# And now.. the "hard" part- calculating correlations between pairs.
# DataFrames corr() function calculates pairwise correlations using specified
# algorithm: 'peason, 'kendall', and 'spearman' are supported.
# Correlations are returned in a new DataFrame instance (corr_df below).
corr_df = final_df.corr(method='pearson')
print("--------------- CORRELATIONS ---------------")
print(corr_df.head(len(dataframes)))
print("--------------- CREATE A HEATMAP ---------------")
# Create a mask to display only the lower triangle of the matrix (since it's mirrored around its
# top-left to bottom-right diagonal).
mask = np.zeros_like(corr_df)
mask[np.triu_indices_from(mask)] = True
# Create the heatmap using seaborn library.
# List if colormaps (parameter 'cmap') is available here: http://matplotlib.org/examples/color/colormaps_reference.html
seaborn.heatmap(corr_df, cmap='RdYlGn_r', vmax=1.0, vmin=-1.0 , mask = mask, linewidths=2.5)
# Show the plot we reorient the labels for each column and row to make them easier to read.
plt.yticks(rotation=0)
plt.xticks(rotation=90)
plt.show()
--------------------------------------------------------------------------------------------------------------
El resultado es la primera imagen, un mapa de calor (heatmap) con las correlaciones cruzadas por colores. Si queremos los datos vamos al visor de Python y podemos copiarlo para exportarlo a Excel o según nos interese.
Ahora entiendo el por qué de Phython.... Lo que no termino de entender es como calculas un coeficiente entre más de 2 series temporales, pues el coeficiente de Pearson está pensando para dos series de datos. Lo veré en detalle.
ResponderEliminarUn abrazo Óscar
ResponderEliminarGracias Manuel, igualmente un fuerte abrazo y bienvenido al nuevo Blog de Notas técnicas para los frikis técnicos del trading :) La matriz de correlaciones son todo parejas, siempre hay que mirar un mercado con otro como bien dices. El principal inconveniente de esto es que si intentas hacerlo en Amibroker no alinea bien las fechas como hace Python (los Dataframes de la librería Pandas son una maravilla para esto). Conozco alguien que lo hizo en Delphi y cuando le metes más de 5 mercados casca.
ResponderEliminarInteresante. Se entiende que los verdes indican correlación negativa también significativa, ¿verdad?
ResponderEliminarPor cierto, ¿dónde se encuentra el "decodificador" de tickers, para identificar cada mercado?
No logro encontrarlo.
Gracias de antemano y adelante!
Efectivamente el verde son fuertes correlaciones inversas. Los tickers están en formato IQFeed. En la tabla que está al final de la siguiente página se puede ver a qué mercados se corresponde cada uno: http://www.onda4.com/files/cartera2018.pdf
EliminarBuenos días, en el código Amibroker hay una nota que dice que "Se exporta lo visible, así que hay que comprimir el gráfico para tener más datos", la pregunta sería con cuántos datos se calcularía la correlación? Supongo que nos interesa la correlación actual (entre 40 y 100 sesiones?), no toda la correlación histórica ya que esta va cambiando con el tiempo.
ResponderEliminarGracias
La nota está mal. Ya la he quitado. Creo recordar que antes funcionaba de otra manera pero he hecho la prueba y los datos que se exportan tienen como origen el "From" del rango de fechas de Amibroker. Las correlaciones se calculan en Python, con el método de Pearson. Se hace sobre todo el dataframe. Para calcular correlaciones a 100 días lo mejor es exportar solo 100 días. Es más fácil que cambiar el código.
ResponderEliminarYo para calcular los últimos 100 días de trading cambio la línea
Eliminarcorr_df = final_df.corr(method='pearson') por corr_df = final_df.tail(100).corr(method='pearson')
Y para a quien le interese ver solo correlaciones superiores a 0.3 debajo de
mask[np.triu_indices_from(mask)] = True
añado esta linea
mask[abs(corr_df)<0.3] = True
Gracias por compartir este gran trabajo
Gracias, son comentarios muy útiles.
EliminarBuenas, Sr. Cagigas.
ResponderEliminarMe parece muy interesante este blog y este post.
Estoy empezando en el trading algorítmico. Estoy aprendiendo mucho de sus libros y posts.
Tengo una duda de novato.
Creo que haber leído que se debe comprobar que la distribución es normal (con algún test de normalidad) para aplicar el coeficiente de Pearson, ya que en caso contrario, proporciona valores erróneos. En esos casos, es recomendable aplicar otro tipo de método como el de Spearman.
También aprovecho para lanzar dos propuestas.
El estudio de correlaciones de calendars spreads (es más complicado por ser números negativos en la mayoría de los casos y ya no cuadra por tener expiraciones). Se podría intentar enlazar las calendars de mínima separación entre meses de cada mercado, y luego aplicar las correlaciones entre las calendars spreads del mes vigente con cada mercado.
Un estudio de matriz de volatilidad diaria( en unidades monetarias) de cada mercado para saber cómo se mueve cada mercado.
Gracias!!!
Un saludo.
Hola. Efectivamente la matemática dice que pearson se aplica si la distribución de precios es normal. Pero los precios no tienen distribución normal, como mucho lognormal (no se permiten valores negativos) aunque tampoco suele ser el caso exacto pues hay "colas largas" en los precios que hacen que movimientos extremos que en teoría serían poco probables acaben teniendo una probabilidad mayor de lo que sería normal; aún así Pearson es lo que se utiliza por los gestores, financieros, etc. Hay muchos modelos en finanzas que no son exactos pero se utilizan igualmente porque son más sencillos o porque no hay otras alternativas mejores.
EliminarNo es un problema que los spreads tengan precios negativos porque la correlación bien hecha debe hacerse entre retornos y no entre precios. Lo que sí que es un problema son las expiraciones de contratos porque no hay una solución óptima. Lo más sencillo es utilizar los futuros continuos cuando están disponibles, pero sabiendo que los precios hacia atrás no coinciden por haber sido ajustados.
En cualquier caso lo importante es hacerse idea de la correlación y no centrase en calcular de forma exacta un número, ya que mañana habrá cambiado por haber cambiado los precios, y depende también del periodo de cálculo, que es arbitrario, así que hablamos de una forma de saber si dos mercados están más o menos acoplados en sus retornos diarios.
Hola de nuevo, Sr. Cagigas.
ResponderEliminarSe me olvidó plantear dos cuestiones más.
1. La comprobación de correlaciones espurias.
2. La variación de la correlación con el tiempo.
1. La comprobación de correlaciones espurias.
Cito apuntes de estadísticas de UCM
Al estimar regresiones entre series no estacionarias es muy fácil que la relación sea
espuria, ya que basta con que ambas series tengan algo de tendencia para que
surja una aparente relación entre ellas
• Al suprimir la tendencia, por ejemplo, diferenciando los datos, la relación espuria
desaparece
Ejemplo, si aumentara la incidencia de melanoma en un caso, cabría esperar un aumento del PNB de 118.981 millones de dólares
Para evitar este tipo de problemas por lo que he leído en estudios estadísticos aplican operaciones tales como eliminar la componente de tendencia a la serie. Y posteriormente aplicar la función de correlación.
O estudiar la cointegración entre las series.
2. Tipos de correlaciones y su variación con el tiempo.
Si el tipo de correlación pudiera ser diferente a la lineal (con una gráfica scatter permite analizar visualmente los posibles tipos de correlaciones).
La variación de la correlación con el tiempo. Se podría hacer con la función rolling . La cuestión a decidir es cuál sería la ventana más apropiada para aplicar el rolling. Supongo que depende de los datos, si son rentabilidades diarias, semanales o mensuales.
EN el caso de datos de retornos mensuales (log_returns.NOMBRE_SUBYACENTE.rolling(12 ().corr ...)
Autores como Louis B. Mendelsohn-Trend Forecasting with Technical Analysis_ Unleashing the Hidden Power of Intermarket Analysis to Beat the Market-Marketplace Books, indican que usar la correlación para medir las relaciones entremercados tiene sus defectos, y apuesta por la aplicación de redes neuronales. Aunque creo que esa idea tiene también un interés comercial al tener un software que hace ese estudio.
Nuevamente gracias.
Un saludo.
Efectivamente las correlaciones espurias son un problema. La única solución que conozco es la experiencia en los mercados. Si Yahoo y el Magro de Cerdo tienen una correlación alta mi experiencia me dice que es espuria, no hay base lógica para que ocurra. Sin embargo yo espero una correlación alta cuando miro los índices entre sí, y también el oro con la plata y el Crudo con el Gasóleo de calefacción. Todos los métodos de cálculo tienen sus defectos. La idea es usarlos con conciencia de ello y filtrar posibles resultados que matemáticamente son correctos pero en la vida real no tienen sentido.
ResponderEliminarBuenas Oscar,
ResponderEliminarLlego un poco tarde a este estupendo post. Me encanta cuando haces tu magia con Amibroker. Solo un apunte para tus lectores, yo sé que tu ya lo conoces y no lo has mencionado para que estudiemos un poco que no se trata de copiar y pegar...
Pearson se usa cuando sus datos se distribuyen normalmente, la variación de los precios tienen una gaussiana platicúrtica y sesgada a la izquierda, lo que significa que tiene colas largas , por tanto usar Pearson NO es apropiado estadísticamente hasta donde yo entiendo la que mejor "hace el trabajo" para el caso de los precios es Spearman y por lo que veo en tu código solo hay que cambiar el parámetro "method"
Mil gracias por tus post.
Gracias por la puntualización, queda claro que conviene cambiar el parámetro tal y como dices. Saludos
Eliminar