Вы находитесь на странице: 1из 46

Introducción a Pandas para análisis de series temporales - Webinar

Este Notebook ha sido elaborado por @Paduel para el grupo de Telegram 'Python para Trading'.

Módulos
Vamos a utilizar el módulo Pandas que permite manejar de forma eficiente datos estructurados. Un DataFrame de Pandas es una matriz ordenada que
puede contener cualquier tipo de datos.

Se ordena en columnas, que deben contener siempre un mismo tipo de dato, y en filas que generan un indice.

Para descargar los datos utilizaremos Pandas Datareader un módulo asociado a Pandas que permite descargar desde distintas fuentes.

Debido a que Yahoo descontinuó su API para datos financieros, para que la descarga desde Yahoo funcione correctamente, debemos aplicarle un
parche, de lo que se ocupa el módulo Fix Yahoo Finance.

Los calculos mas complejos los resolveremos con el módulo Numpy.

Para las gráficas inicialmente utilizaremos la librería Matplotlib.

Y para utilizar datos temporales adecuadamente Datetime.

Si quieres correr este código, debes asegurarte tener instalados correctamente los paquetes mencionados.

In [67]:
import pandas as pd # Importamos Pandas y para simplificar le asignamos el nombre pd

pd.core.common.is_list_like = pd.api.types.is_list_like
from pandas_datareader import data as pdr
import fix_yahoo_finance as yf
yf.pdr_override() # Importamos Pandas Datareader y lo parcheamos para que funcione con Yahoo
import numpy as np # Importamos Numpy y le asociamos el nombre np

import datetime # Importamos datetime pero en este caso no vamos a asignarle ningún nombre

# Para graficar correctamente en el notebook hacemos lo siguiente


%matplotlib inline
# esto hace que los gráficos se visualicen en el notebook
import matplotlib.pyplot as plt
import matplotlib.pylab as pylab
pylab.rcParams['figure.figsize'] = 18, 8 # Definimos el tamaño de los gráficos para que se ajusten bien al notebook
plt.style.use('ggplot')
In [68]:
pylab.rcParams['figure.figsize'] = 18, 8 # Definimos el tamaño de los gráficos para que se ajusten bien al notebook

Descarga de datos
Descargaremos el histórico diario de Santander y BBVA desde principios de 2008 hasta final de 2017, diez años. Para ello pasaremos como parámetros
los tickers de los valores y las fechas de inicio y fin. Los tickers para otros valores se pueden localizar en la página de Yahoo.

In [69]:
start = datetime.datetime(2008, 1, 1)
end=datetime.datetime(2018, 8, 28)
tickers=['SAN.MC', 'BBVA.MC']
In [70]:
san = pdr.get_data_yahoo(tickers[0], start=start, end=end)
[*********************100%***********************] 1 of 1 downloaded
/home/argante/anaconda2/envs/mypython3/lib/python3.6/site-packages/ipykernel/__main__.py:1: FutureWarning:
Panel is deprecated and will be removed in a future version.
The recommended way to represent these types of 3-dimensional data are with a MultiIndex on a DataFrame, via the
Panel.to_frame() method
Alternatively, you can use the xarray package http://xarray.pydata.org/en/stable/.
Pandas provides a `.to_xarray()` method to help automate this conversion.

if __name__ == '__main__':
/home/argante/anaconda2/envs/mypython3/lib/python3.6/site-packages/fix_yahoo_finance/__init__.py:199: FutureWarning:
Panel is deprecated and will be removed in a future version.
The recommended way to represent these types of 3-dimensional data are with a MultiIndex on a DataFrame, via the
Panel.to_frame() method
Alternatively, you can use the xarray package http://xarray.pydata.org/en/stable/.
Pandas provides a `.to_xarray()` method to help automate this conversion.

data = data.swapaxes(0, 2)
In [71]:
bbva=pdr.get_data_yahoo(tickers[1], start=start, end=end)
[*********************100%***********************] 1 of 1 downloaded
/home/argante/anaconda2/envs/mypython3/lib/python3.6/site-packages/ipykernel/__main__.py:1: FutureWarning:
Panel is deprecated and will be removed in a future version.
The recommended way to represent these types of 3-dimensional data are with a MultiIndex on a DataFrame, via the
Panel.to_frame() method
Alternatively, you can use the xarray package http://xarray.pydata.org/en/stable/.
Pandas provides a `.to_xarray()` method to help automate this conversion.

if __name__ == '__main__':
/home/argante/anaconda2/envs/mypython3/lib/python3.6/site-packages/fix_yahoo_finance/__init__.py:199: FutureWarning:
Panel is deprecated and will be removed in a future version.
The recommended way to represent these types of 3-dimensional data are with a MultiIndex on a DataFrame, via the
Panel.to_frame() method
Alternatively, you can use the xarray package http://xarray.pydata.org/en/stable/.
Pandas provides a `.to_xarray()` method to help automate this conversion.

data = data.swapaxes(0, 2)

Lo que recibimos en la variable san es un DataFrame de Pandas, una matriz de dos dimensiones, algo similar al un hoja de calculo de Excel.

Inspección de los datos recibidos


In [72]:
san.describe()
Out[72]:
Open High Low Close Adj Close Volume
count 2723.000000 2723.000000 2723.000000 2723.000000 2723.000000 2.723000e+03
mean 6.723748 6.815643 6.615799 6.719774 4.426379 9.759697e+07
std 2.108875 2.135027 2.074300 2.107484 0.890322 7.474837e+07
min 3.117870 3.327370 3.098200 3.245730 1.790819 7.377082e+06
25% 5.328945 5.397885 5.265465 5.332935 3.704867 5.438911e+07
50% 6.098050 6.181650 5.977070 6.090180 4.540500 7.630015e+07
75% 7.834725 7.964715 7.690765 7.817795 5.223733 1.144074e+08
max 13.248900 13.330200 13.095300 13.176600 6.051420 9.092368e+08
In [73]:
bbva.describe()
Out[73]:
Open High Low Close Adj Close Volume
count 2723.000000 2723.000000 2723.000000 2723.000000 2723.000000 2.723000e+03
mean 8.064624 8.170884 7.938805 8.060340 5.953328 5.289962e+07
std 2.085744 2.101786 2.061376 2.082301 1.194432 4.678322e+07
min 4.400000 4.540000 4.275310 4.430000 2.677433 5.217450e+06
25% 6.480000 6.554500 6.375500 6.476500 4.993867 2.722019e+07
50% 7.680000 7.777000 7.551000 7.666740 5.973335 3.936453e+07
75% 9.084500 9.196500 8.952565 9.074000 7.008568 6.124712e+07
max 16.102100 16.159700 15.852300 15.929100 8.746736 6.009019e+08
In [74]:
bbva.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2723 entries, 2008-01-02 to 2018-08-28
Data columns (total 6 columns):
Open 2723 non-null float64
High 2723 non-null float64
Low 2723 non-null float64
Close 2723 non-null float64
Adj Close 2723 non-null float64
Volume 2723 non-null int64
dtypes: float64(5), int64(1)
memory usage: 148.9 KB
In [75]:
bbva.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2723 entries, 2008-01-02 to 2018-08-28
Data columns (total 6 columns):
Open 2723 non-null float64
High 2723 non-null float64
Low 2723 non-null float64
Close 2723 non-null float64
Adj Close 2723 non-null float64
Volume 2723 non-null int64
dtypes: float64(5), int64(1)
memory usage: 148.9 KB
In [76]:
bbva.head() # comprobamos los primeros datos de BBVA
Out[76]:
Open High Low Close Adj Close Volume
Date
2008-01-02 16.1021 16.1597 15.8523 15.9291 8.746736 98910228
2008-01-03 15.9291 15.9291 15.6986 15.7946 8.672879 90699092
2008-01-04 15.7946 15.7946 15.3239 15.4680 8.493544 138136794
2008-01-07 15.4680 15.5929 15.3719 15.5352 8.530444 32842185
2008-01-08 15.5352 15.5833 15.1990 15.2374 8.366920 87804410

Revisamos las dimensiones de cada dataframe, vemos que tienen dos dimensiones.
2723 filas correspondientes a los días de cotización, y 6 columnas para los distintos datos.

In [77]:
san.shape, bbva.shape
Out[77]:
((2723, 6), (2723, 6))

Veamos algunas características del dataframe del Santander.

In [78]:
san.index # su indice son las fechas de los dias cotizados
Out[78]:
DatetimeIndex(['2008-01-02', '2008-01-03', '2008-01-04', '2008-01-07',
'2008-01-08', '2008-01-09', '2008-01-10', '2008-01-11',
'2008-01-14', '2008-01-15',
...
'2018-08-15', '2018-08-16', '2018-08-17', '2018-08-20',
'2018-08-21', '2018-08-22', '2018-08-23', '2018-08-24',
'2018-08-27', '2018-08-28'],
dtype='datetime64[ns]', name='Date', length=2723, freq=None)
In [79]:
san.columns # las columnas recogen los distintos valores de cada dia
Out[79]:
Index(['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume'], dtype='object')

Podemos acceder a la información seleccionando los datos de múltiples formas.

In [80]:
san[['Close', 'Low', 'Volume']][-10:] # Veamos los diez últimos valores de cierre del Santander.
Out[80]:
Close Low Volume
Date
2018-08-15 4.3355 4.3000 46449338
Close Low Volume
Date
2018-08-16 4.3695 4.3385 35213008
2018-08-17 4.3395 4.3110 42357176
2018-08-20 4.3495 4.3345 19670206
2018-08-21 4.4000 4.3425 30725386
2018-08-22 4.3525 4.3455 65903607
2018-08-23 4.3570 4.3300 21502679
2018-08-24 4.3500 4.3450 20338430
2018-08-27 4.3850 4.3430 16468482
2018-08-28 4.3620 4.3350 31992133
In [81]:
san[['High','Low']].head() # Los 5 primeros mínimos y máximos
Out[81]:
High Low
Date
2008-01-02 13.3302 13.0953
2008-01-03 13.2037 12.9689
2008-01-04 13.0863 12.7070
2008-01-07 12.9057 12.6889
2008-01-08 12.8063 12.4993
In [82]:
san[['Adj Close','Volume']].loc['2015-11-01':'2015-11-10']
# El cierre ajustado y el volumen desde el 1 al 10 de noviembre de 2015
Out[82]:
Adj Close Volume
Date
2015-11-02 4.391398 57910508
2015-11-03 4.434068 148301916
2015-11-04 4.501484 105920463
2015-11-05 4.443457 87484985
2015-11-06 4.527079 86906806
2015-11-09 4.431509 71676192
2015-11-10 4.416143 39123436

Estos son algunos ejemplos de la facilidad que nos proporciona Pandas para segmentar los datos. Cualquier combinación de filtros sería posible.

In [83]:
san.Close.plot()
Out[83]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3805011a90>
In [84]:
san.Close.plot(figsize=(18,6))
plt.show()
san.Volume.plot(c='b', figsize=(18,4))
Out[84]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3805271a58>
Usando los datos
Vamos a ver como podemos utilizar los datos para generar un nuevo dataframe y después hacer una gráfica sencilla.

Tomamos los datos de cierre ajustado de ambos valores con ellos creamos el dataframe diario.

In [85]:
diario=pd.DataFrame() # creamos un dataframe vacío
diario['san']=san['Adj Close'] # añadimos una columna con los datos de cierre del Santander y la nombramos 'san'
diario['bbva']=bbva['Adj Close'] # lo mismo para el BBVA
In [86]:
diario.head(7) # veamos su cabecera
Out[86]:
san bbva
Date
2008-01-02 5.595079 8.746736
2008-01-03 5.541409 8.672879
2008-01-04 5.460856 8.493544
2008-01-07 5.426378 8.530444
2008-01-08 5.342005 8.366920
2008-01-09 5.234619 8.261382
2008-01-10 5.173260 8.159203
In [87]:
diario.tail(7) # y su final
Out[87]:
san bbva
Date
2018-08-20 4.3495 5.416
2018-08-21 4.4000 5.496
2018-08-22 4.3525 5.551
2018-08-23 4.3570 5.522
2018-08-24 4.3500 5.533
2018-08-27 4.3850 5.571
2018-08-28 4.3620 5.490

Creamos la gráfica con el cierre ajustado de ambos valores.

Pandas dispone de sus propios metodos para llamar al modulo Matplotlib, de forma que graficar un dataframe es muy sencillo.
In [88]:
diario.plot()
Out[88]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f38052f41d0>

In [89]:
san['Adj Close'].plot()
bbva['Adj Close'].plot()
Out[89]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f380514d0f0>
Podemos ver la gráfica de solo un año.

In [90]:
diario['2012'].plot()
Out[90]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f38050fe0f0>
Remuestreo de datos
Los datos que tenemos son diarios, pero tal vez nos interesa la evolución mensual.

Pandas nos ofrece la función resample para hacer un remuestreo de los datos para una frecuencia temporal distinta.
Incluyendo ohlc() nos va a calcular la apertura, máximo, mínimo y cierre de una serie. Tomemos la serie de cierres del Santander para verlo

In [91]:
san.Close.resample('M').ohlc().head() # Usamos head() para visualizar solo los 5 primeros valores de la tabla.
Out[91]:
open high low close
Date
2008-01-31 13.1766 13.1766 10.1782 10.6840
2008-02-29 10.9007 11.1265 10.2505 10.7743
2008-03-31 10.5666 11.5239 10.0879 11.3975
2008-04-30 11.9213 12.5083 11.4968 12.5083
2008-05-31 12.7341 12.8425 11.8129 12.0929

Sin embargo toma el primer cierre, y el máximo y mínimo del cierre del mes. Pero si nos interesa la verdadera apertura del mes, el máximo y mínimos
reales de la cotización del mes, y no los valores máximo y mínimo del cierre, podemos calcularlo de otra manera.

Utilizando la función resample de pandas e indicándole como hacer el remuestreo para cada columna podemos hacerlo fácilmente.

En este caso definiremos una función para hacer el remuestreo a cualquier dataframe de las mismas características.

In [92]:
def remuestreo(df, tiempo):
df=df.resample(tiempo).agg({'Open': 'first',
'High': 'max',
'Low': 'min',
'Close': 'last',
'Adj Close': 'last',
'Volume': 'sum'})
return df

Las funciones nos permiten aplicar un mismo proceso a distintas variables.


In [93]:
san_mes=remuestreo(san,'M')
bbva_mes=remuestreo(bbva, 'M')

Veamos los primeros meses de BBVA.

In [94]:
bbva_mes.head()
Out[94]:
Open High Low Close Adj Close Volume
Date
2008-01-31 16.1021 16.1597 11.9228 13.5177 7.498380 1649477521
2008-02-29 13.5177 14.1902 12.5857 13.2198 7.333133 845263801
2008-03-31 13.2198 13.6810 12.1726 13.4024 7.434424 981652411
2008-04-30 13.4024 14.4304 13.2775 14.1902 8.030556 1561042865
2008-05-31 14.1902 14.7955 13.6426 13.7675 7.791340 538775611
In [95]:
san.tail()
Out[95]:
Open High Low Close Adj Close Volume
Date
2018-08-22 4.390 4.4045 4.3455 4.3525 4.3525 65903607
2018-08-23 4.369 4.3865 4.3300 4.3570 4.3570 21502679
2018-08-24 4.345 4.3880 4.3450 4.3500 4.3500 20338430
2018-08-27 4.374 4.3945 4.3430 4.3850 4.3850 16468482
2018-08-28 4.405 4.4150 4.3350 4.3620 4.3620 31992133

Podemos hacer otro remuestreo con un valor temporal distinto simplemente cambiando el valor de tiempo por otro como '3M', 'W', '2Y'.
Agrupando
La función groupby nos permite agrupar los valores de un DataFrame en base a un cierto valor, para realizar un calculo sobre esos grupos.

En este ejemplo con una sola linea graficamos los retornos diarios medios del Santander por día de la semana.

In [96]:
san.Close.pct_change().groupby(san.index.dayofweek).mean().plot(kind='bar')
Out[96]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f380507d8d0>
Vemos que de media los lunes los retornos son negativos, frente al resto de días de la semana que son positivos, aunque su media es mas cercana a cero.

Trazamos el mismo gŕafico para el BBVA, y vemos que sucede algo similar.

In [97]:
bbva.Close.pct_change().groupby(san.index.dayofweek).mean().plot(kind='bar')
Out[97]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f38052aa160>
Añadiendo y modificando columnas
Hasta ahora hemos trabajado con las columnas de los datos que descargamos, creemos nuevas columnas con datos calculados a partir de los iniciales.

Vamos a calcular la apertura, máximo y mínimo ajustado de la serie temporal. De nuevo definimos una función para reutilizar el código.
In [98]:
def ajustado(df, lista):
df=df.copy()
for f in lista:
df['Adj '+f]=df[f]*df['Adj Close']/df['Close']
return df
In [99]:
san_ajustado=ajustado(san, ['Open','High','Low'])
In [100]:
san_ajustado.head()
Out[100]:
Open High Low Close Adj Close Volume Adj Open Adj High Adj Low
Date
2008-01-02 13.2489 13.3302 13.0953 13.1766 5.595079 103998100 5.625779 5.660301 5.560557
2008-01-03 13.1405 13.2037 12.9689 13.0502 5.541409 113222703 5.579752 5.606589 5.506887
2008-01-04 13.0050 13.0863 12.7070 12.8605 5.460856 100543802 5.522214 5.556736 5.395676
2008-01-07 12.8154 12.9057 12.6889 12.7793 5.426378 71995342 5.441707 5.480050 5.387992
2008-01-08 12.7883 12.8063 12.4993 12.5806 5.342005 83100401 5.430199 5.437842 5.307483

No está mal. Pero hagamos que la función elimine los valores no ajustados y ordene correctamente las columnas.

Para comodidad eliminaremos de las columnas el prefijo Adj, ya sabemos que los valores que quedan están ajustados.

Estas funciones pueden ser copiadas para utilizar en otro código o con otros valores, por lo que merece la pena utilizar un poco de tiempo en definir
funciones de las tareas que mas vamos a repetir.

O incluso recogerlas en un modulo personal con herramientas o utilidades.

In [101]:
def ajustado(df):
df=df.copy()
lista=['Open','High','Low']

for f in lista:
df[f]=df[f]*df['Adj Close']/df['Close']
df=df.drop('Close', axis=1)
df=df.rename(columns = {'Adj Close': 'Close'} )
return df
In [102]:
san=ajustado(san)
san.head()
Out[102]:
Open High Low Close Volume
Date
2008-01-02 5.625779 5.660301 5.560557 5.595079 103998100
2008-01-03 5.579752 5.606589 5.506887 5.541409 113222703
2008-01-04 5.522214 5.556736 5.395676 5.460856 100543802
2008-01-07 5.441707 5.480050 5.387992 5.426378 71995342
2008-01-08 5.430199 5.437842 5.307483 5.342005 83100401
In [103]:
bbva = ajustado(bbva)
In [104]:
bbva.head()
Out[104]:
Open High Low Close Volume
Date
2008-01-02 8.841731 8.873359 8.704565 8.746736 98910228
2008-01-03 8.746733 8.746733 8.620165 8.672879 90699092
2008-01-04 8.672881 8.672881 8.414418 8.493544 138136794
2008-01-07 8.493544 8.562127 8.440775 8.530444 32842185
Open High Low Close Volume
Date
2008-01-08 8.530443 8.556855 8.345834 8.366920 87804410

Añadiendo indicadores
Los indicadores son una herramienta básica en el análisis de series temporales financieras.

Veamos como añadir una media simple usando solo Pandas.

In [105]:
san['SMA_7']=san['Close'].rolling(window=7).mean() # Media simple de 7 días
san['SMA_14']=san['Close'].rolling(window=14).mean() # Media simple de 14 días
In [106]:
san.head(25)
Out[106]:
Open High Low Close Volume SMA_7 SMA_14
Date
2008-01-02 5.625779 5.660301 5.560557 5.595079 103998100 NaN NaN
2008-01-03 5.579752 5.606589 5.506887 5.541409 113222703 NaN NaN
2008-01-04 5.522214 5.556736 5.395676 5.460856 100543802 NaN NaN
2008-01-07 5.441707 5.480050 5.387992 5.426378 71995342 NaN NaN
2008-01-08 5.430199 5.437842 5.307483 5.342005 83100401 NaN NaN
2008-01-09 5.295977 5.318991 5.226933 5.234619 120300001 NaN NaN
2008-01-10 5.249947 5.261454 5.134917 5.173260 132219917 5.396229 NaN
2008-01-11 5.234618 5.284469 5.146424 5.257633 103739924 5.348023 NaN
Open High Low Close Volume SMA_7 SMA_14
Date
2008-01-14 5.257632 5.269139 5.146424 5.184767 164859458 5.297074 NaN
2008-01-15 5.184769 5.184769 4.985366 4.985366 137807143 5.229147 NaN
2008-01-16 4.985366 4.996831 4.854965 4.927830 209551671 5.157926 NaN
2008-01-17 4.927829 5.046681 4.851143 4.889486 153060632 5.093280 NaN
2008-01-18 4.889485 4.924007 4.759084 4.770591 315516105 5.026990 NaN
2008-01-21 4.770592 4.770592 4.283550 4.341086 279633814 4.908108 5.152169
2008-01-22 4.341085 4.578831 4.026627 4.540488 226870977 4.805659 5.076841
2008-01-23 4.540487 4.628681 4.222202 4.321891 145491908 4.682391 4.989733
2008-01-24 4.321892 4.636368 4.321892 4.605710 206460314 4.628155 4.928651
2008-01-25 4.605709 4.759083 4.594202 4.636367 173544339 4.586517 4.872221
2008-01-28 4.636369 4.636369 4.482996 4.598026 151191340 4.544880 4.819080
2008-01-29 4.598024 4.655560 4.563502 4.624860 99984519 4.524061 4.775526
2008-01-30 4.624860 4.636368 4.563503 4.605710 99493718 4.561865 4.734986
2008-01-31 4.605711 4.605711 4.417773 4.536667 172381924 4.561319 4.683489
2008-02-01 4.542910 4.682562 4.542910 4.682562 119563494 4.612843 4.647617
2008-02-04 4.682564 4.709755 4.620491 4.651549 190329873 4.619392 4.623773
2008-02-05 4.651549 4.651549 4.387753 4.403260 164008370 4.586091 4.586304

Aparecen valores nulos NaN , pues hasta que no tiene datos suficientes no puede calcular la media.

Graficamos los últimos 500 valores para observar las medias.

In [107]:
san[['Close','SMA_7','SMA_14']].iloc[-500:].plot()
Out[107]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3805250908>

Pandas nos ofrece la oportunidad de crear otros indices mas complejos. Sin embargo si queremos facilitar el trabajo existe un modulo con indicadores
para pandas llamado Technical Analysis Library, https://github.com/bukosabino/ta, que nos proporciona la forma de calcular muchos indices.

Se puede instalar con pip install ta.

In [108]:
import ta

Calculemos ahora las medias exponenciales de 20 y 60 días.

In [109]:
san['EMA_20'] = ta.ema(san['Close'], 20)
san['EMA_60'] = ta.ema(san['Close'], 60)

Gráficamos los 1000 primeros días.

In [110]:
san[['Close','EMA_20','EMA_60']].iloc[:1000].plot()
Out[110]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3804ee3c50>
Podemos definir una función para realizar un calculo o tarea que vamos a repetir varías veces, haciendo el código mas eficiente.

Por ejemplo para calcular y añadir al DataFrame varias EMAs con distintos periodos.

In [111]:
def add_emas (df, periods, close='Close'):
# Definimos la función para aceptar un DataFrame, una lista de periodos y por defecto usar la columna Close.
import ta
for period in periods:
name = f'EMA_{period}'
df[name] = ta.ema(df[close], period)
return df

Ahora pasamos a la función la tabla del Santader y una lista de periodos para el EMA.

In [112]:
periodos = [7,14,28,60,90,120,180,200,260,500]
san_emas = add_emas(san, periods=periodos)

Podemos ver como se han añadido nuevas columnas con los EMAs calculados, y graficarlos.

In [113]:
san_emas.head()
Out[113]:
Volum SMA SMA_ EMA EMA EMA EMA EMA EMA EMA_ EMA_ EMA_ EMA_ EMA_
Open High Low Close
e _7 14 _20 _60 _7 _14 _28 _90 120 180 200 260 500
Dat
e
200
8- 5.6257 5.6603 5.5605 5.5950 103998 5.5950 5.5950 5.5950 5.5950 5.5950 5.5950 5.5950 5.5950 5.5950 5.5950 5.5950
NaN NaN
01- 79 01 57 79 100 79 79 79 79 79 79 79 79 79 79 79
02
200
8- 5.5797 5.6065 5.5068 5.5414 113222 5.5675 5.5680 5.5664 5.5673 5.5677 5.5680 5.5681 5.5681 5.5681 5.5681 5.5682
NaN NaN
01- 52 89 87 09 703 89 22 55 19 73 96 33 70 77 92 17
03
200
8- 5.5222 5.5567 5.3956 5.4608 100543 5.5302 5.5317 5.5264 5.5293 5.5308 5.5319 5.5320 5.5322 5.5322 5.5322 5.5323
NaN NaN
01- 14 36 76 56 802 62 08 65 57 76 53 77 00 25 76 59
04
Volum SMA SMA_ EMA EMA EMA EMA EMA EMA EMA_ EMA_ EMA_ EMA_ EMA_
Open High Low Close
e _7 14 _20 _60 _7 _14 _28 _90 120 180 200 260 500
Dat
e
200
8- 5.4417 5.4800 5.3879 5.4263 719953 5.5023 5.5047 5.4962 5.5008 5.5033 5.5051 5.5053 5.5055 5.5055 5.5056 5.5057
NaN NaN
01- 07 50 92 78 42 61 19 32 90 61 21 22 24 65 49 84
07
200
8- 5.4301 5.4378 5.3074 5.3420 831004 5.4670 5.4710 5.4566 5.4645 5.4687 5.4717 5.4721 5.4724 5.4725 5.4726 5.4728
NaN NaN
01- 99 42 83 05 01 86 92 54 84 86 72 14 57 26 68 97
08
In [114]:
emas = [f'EMA_{p}' for p in periodos]
san_emas[emas].plot()
Out[114]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3804e798d0>
Como último ejemplo de indicador calculemos el ATR de 7 días y lo graficamos junto con el precio de cierre.

In [115]:
san['ATR_7'] = ta.average_true_range(san.High, san.Low, san.Close, n=7)
In [116]:
# Usamos subplot para obtener dos gráficos juntos y hacemos que compartan el eje de X.
ax1 = plt.subplot(211)
san['2016':].Close.plot()
ax1 = plt.subplot(212)
san['2016':].ATR_7.plot(c='g', sharex=ax1)
Out[116]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3804ddec50>

Relaciones entre los dos valores.


Veamos ahora que tal se relacionan algunos valores de estas dos acciones.
Comenzamos por calcular los retornos diarios.

In [117]:
san['rets'] = san.Close.pct_change()
bbva['rets'] = bbva.Close.pct_change()

Podemos compararlos usando un histograma conjunto.

In [118]:
san.rets.hist(bins=30, alpha=0.7, color='r')
bbva.rets.hist(bins=30, alpha=0.7, color='b')
plt.legend(['SAN','BBVA'])
Out[118]:
<matplotlib.legend.Legend at 0x7f3804cc68d0>
Podemos comparar de esta manera cualquier variable que calculemos de los activos, como por ejemplo los gaps entre el cierre y apertura del siguiente
día.

In [119]:
san['Gap'] = (san.Open - san.Close.shift(1))/ san.Close.shift(1)
bbva['Gap'] = (bbva.Open - bbva.Close.shift(1))/ bbva.Close.shift(1)
san.Gap.hist(bins=30, alpha=0.7, color='r')
bbva.Gap.hist(bins=30, alpha=0.7, color='b')
plt.legend(['SAN','BBVA'])
Out[119]:
<matplotlib.legend.Legend at 0x7f3804bc1048>

Usando un gráfico de dispersión podemos visualizar la relación entre un gap y la diferencia entre apertura y cierre del día.

In [120]:
plt.scatter(san.Gap, (san.Close-san.Open)/san.Open)
Out[120]:
<matplotlib.collections.PathCollection at 0x7f3804abcc50>

Y calcular el valor de la correlación entre ambos valores, usando la función corr de Pandas.

Para comprobar que, como nos indica el gráfico, apenas existe correlación, pues el valor esta cercano a cero.

In [121]:
san.Gap.corr((san.Close-san.Open)/san.Open)
Out[121]:
-0.071468376267038725

Calculemos la correlación entre los retornos diarios de ambos valores.

In [122]:
san.rets.corr(bbva.rets)
Out[122]:
0.92446301175977774

Vemos que la correlación es positiva y cercana a 1, luego podría existir una correlación positiva entre los retornos.

Veámoslo graficando las series de retornos superpuestas.

In [123]:
san.rets.plot(alpha=0.3) # alpha define el grado de transparencia.
bbva.rets.plot(alpha=0.2)
plt.legend()
Out[123]:
<matplotlib.legend.Legend at 0x7f3804a68c88>
En este gráfico vemos la diferencia entre los retornos del Santander y los del BBVA. Ademas añadimos la media simple de 22 días de está diferencia.

Podemos comprobar como la media es cercana a cero aunque con cierta oscilación.

In [124]:
(san.rets-bbva.rets).plot()
(san.rets-bbva.rets).rolling(22).mean().plot()
Out[124]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f38049f3b70>
Vemos ahora la evolución de la correlación en el tiempo calculando la misma para una ventana de desplazamiento o rodante de 90 días.

Vemos como no es constante, siendo en algunos periodos mas cercana a 1, que en otros. Desde 2017 parece haberse deteriorado esa correlación.

In [125]:
san.rets.rolling(90).corr(bbva.rets.rolling(90)).plot()
Out[125]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f38049f3dd8>
Un gráfico de dispersión nos muestra como los retornos se agrupan alrededor de una posible linea.

In [126]:
plt.scatter(san.rets, bbva.rets)
Out[126]:
<matplotlib.collections.PathCollection at 0x7f38048be240>
Para hacer esto mas evidente, calculamos y graficamos esa linea, que no es otra cosa que una regresión lineal.

Para ello vamos a usar el módulo Seaborn, que tiene funciones mas potentes que Matplotlib.

En este caso usamos la función regplot, que sobre el gráfico de dispersión traza la linea de la regresión lineal calculada.

In [127]:
import seaborn as sns
In [128]:
sns.regplot(san.rets, bbva.rets, order=1)
Out[128]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3804ac79e8>

Trazamos ahora un gráfico de dispersión pero con los valores de cierre.

In [129]:
plt.scatter(san.Close, bbva.Close)
Out[129]:
<matplotlib.collections.PathCollection at 0x7f38047f8978>

Podemos usar la función corr para calcular la correlación entre los valores de las columnas de un DataFrame.

En el DataFrame diario tenemos los precios de cierre ajustado de ambos valores, veamos la correlación.

In [130]:
diario.corr()
Out[130]:
san bbva
san 1.000000 0.912283
bbva 0.912283 1.000000

Usamos de nuevo regplot para trazar la linea de regresión lineal.

Vemos que en esta ocasión los valores no se encuentran tan agrupados cerca de la linea.

In [131]:
sns.regplot(san.Close, bbva.Close, order=1)
Out[131]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f38047a43c8>
Por último trazamos un gráfico para ver como se distribuyen los precios de cierres del Santander frente al BBVA a lo largo del tiempo, usando para ello
el mismo gráfico de dispersión pero añadiendo un mapa de color en función del tiempo.

Vemos como los precios aparecen mas agrupados según los años.

In [132]:
years=san.index.year.unique()
fig = plt.figure(figsize=(20,8))
scatter=plt.scatter(san.Close, bbva.Close, alpha = .6, c = san.index, cmap = 'viridis')
cb = plt.colorbar(extend='both')
cb.ax.set_yticklabels(years)
plt.show()

https://github.com/paduel/Python_para_Trading/blob/master/Pandas%20para%20Valores/Webinar%20-
%20Introducci%C3%B3n%20a%20Pandas%20para%20an%C3%A1lisis%20de%20series%20temporales%20v.0.1.ipynb

Вам также может понравиться