Tabla de retornos

Gerard Sánchez - 12 de Diciembre de 2021 a las 19:56 - Python




El objetivo de este artículo es detallar paso a paso la construcción de una tabla a partir de cuatro criterios: La rentabilidad diaria (Open-Close), la rentabilidad con respecto al día anterior (Close-Close), la rentabilidad anual y el capital negociado (Open+Close/2*Volume) transformado en miles.

La idea es hacerlo operativo para que un usuario sin extensos conocimientos sea capaz de ordenarlo según su propio criterio siguiendo una interfaz CLI sencilla.

Partimos de un archivo csv, que contiene nombres de empresa y tickers llamado Ibex.csv que tendremos que adjuntar en el mismo directorio donde ejecutemos el programa.

Vamos al código:

import pandas_datareader.data as wb
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import yfinance as yf

Vamos a necesitar datetime para este ejemplo. Jugando con fechas, necesitamos descargar un periodo de datos prudencial (7 días) para no toparnos con festivos y poder calcular la rentabilidad entre días, escogiendo los últimos de los 7. Más adelante lo veremos.

emp=pd.read_csv('Ibex.csv' ,header= None)
emp.rename(columns={0:'Nombre', 1:'Ticker'}, inplace=True)

En una variable emp cargamos el DataFrame del csv y renombramos las columnas, siendo la primera "Nombre" y la segunda "Ticker".

print('\n','BOLSA IBEX 35\n','\n Ordenar por:','\n1.- Rentabilidad sobre cierre anterior','\n2.- Rentabilidad del día'
    ,'\n3.- Rentabilidad 52 semanas','\n4.- Capital Negociado (Open+Close)/2')

El menú es el que podéis observar arriba. Tenemos cuatro posibilidades.

try:
    o=int(input('\nEscoge número para ordenación: '))
    if (o<0 or o>4):
        print('\nAlfabéticamente')
except:
    o=5 

tipo=str(input('\nAscendente o Descendente (A/D) ').upper())
if tipo!='A':
    tipo='D'

La idea es poder ordenarlo según la opción que decidamos y en función de si lo queremos de forma ascendente/descendente o alfabéticamente. El except lo utilizo para poder saltar a la condición de orden alfabético poniendo o=5.

th=datetime.today()
td=timedelta(days=7)
td1=timedelta(days=365)

ta=th-td
ta1=th-td1-td
ta2=ta1+td

ta=ta.strftime('%Y-%m-%d')
ta1=ta1.strftime('%Y-%m-%d')
ta2=ta2.strftime('%Y-%m-%d')

A continuación y partiendo de tener en variables la fecha de hoy, el periodo de una semana con timedelta y el periodo anual, realizo operaciones aritméticas para tener una semana de periodo anterior al de hoy, y una semana menos un año anterior al día de hoy para poder realizar el cálculo anual más adelante.

tablat = pd.DataFrame()

for i in range(len(emp)):
    print(emp.iloc[i,0])

    tabla = yf.download(emp.iloc[i,1],ta,th, progress=False)[['Open', 'Close', 'Volume']]
    for w in range(len(tabla)-2):
        tabla = tabla.drop(tabla.index[0])

    ap = yf.download(emp.iloc[i,1],ta1, ta2, progress=False)[['Open','Close','Volume']]
    for w in range(len(ap)-2):
        ap = ap.drop(ap.index[0])

Primero declaramos un DataFrame con el que iremos juntando la información.

Un bucle iterativo partiendo del tamaño exacto de empresas/tickers, 35 en este caso, para realizar la descarga ticker a ticker de las columnas Open, Close y Volume.

Una semana hacia atrás partiendo de hoy para tabla y un año menos una semana hacia atrás para ap.

El bucle con un rango de (-2) es para eliminar con el método drop 5 de las 7 filas descargadas, ya que únicamente necesitamos los dos últimos para el cálculo en ambos casos. Escogemos 7 por tener un periodo prudencial de datos para evitar fallos en la descarga y poder contar siempre con 2 filas independientemente de festivos.

    tabla['% dia']=0
    tabla['% C. ant.']=0
    tabla['% 52 sem.']=0
    tabla['Capital(m)']=0
    tabla['Compañia']= emp.iloc[i,0]
    tabla['Ticker']= emp.iloc[i,1]

Generamos nuevas columnas a partir de las ya habidas (Open, Close, Volume) y poder rellenarlas a continuación con los cálculos pertinentes.

    tabla.iloc[1,0]= round(tabla.iloc[1,0],2)
    tabla.iloc[1,1]= round(tabla.iloc[1,1],2)
    tabla.iloc[1,3]= round(((tabla.iloc[1,1]/tabla.iloc[1,0])-1)*100,2)
    tabla.iloc[1,4]= round(((tabla.iloc[1,1]/tabla.iloc[0,1])-1)*100,2)
    tabla.iloc[1,5]= round(((tabla.iloc[1,1]/ap.iloc[1,1])-1)*100,2)
    tabla.iloc[1,6]= round((tabla.iloc[1,0]+tabla.iloc[1,1])*tabla.iloc[1,2]*0.0005,2)

Rellenamos con iloc en base a posiciones concretas para filas y columnas, haciendo el cálculo porcentual entre el Open-Close, Close-Close, Close-Close anual y el capital negociado en miles (*0,0005).

    tabla=tabla.drop(['Volume'], axis=1)
    tabla=tabla.drop(tabla.index[0])
    tablat=pd.concat([tablat,tabla], axis=0)

Eliminamos la columna de Volumen que ya no nos sirve, eliminamos la primera fila con drop que usábamos para el cálculo y con concat, todo de forma iterativa para los 35 tickers, vamos generando nuestro DataFrame tablat.

tablat = tablat.set_index('Ticker')

if tipo=='D':
    if o==1:
        print(tablat.sort_values(by='% C. ant.', ascending=False))
    elif o==2:
        print(tablat.sort_values(by = '% dia', ascending=False))
    elif o==4:
        print(tablat.sort_values(by = 'Capital(m)', ascending=False))
    elif o==3:
            print(tablat.sort_values(by = '% 52 sem.', ascending=False))
    else:
        print(tablat.sort_values(by='Compañia', ascending=False))
elif tipo=='A':
    if o==1:
        print(tablat.sort_values(by='% C. ant.', ascending=True))
    elif o==2:
        print(tablat.sort_values(by = '% dia', ascending=True))
    elif o==4:
        print(tablat.sort_values(by = 'Capital(m)', ascending=True))
    elif o==3:
        print(tablat.sort_values(by = '% 52 sem.', ascending=True))
    else:
       print(tablat.sort_values(by='Compañia', ascending=True))

Ponemos como índice los tickers y delimitamos las elecciones del usuario con el método sort_values() para poder ordenar cada columna en función del input() anterior.

while True:
    try:
        graf=str(input('\nTicker a representar evolución: ').upper())
        if graf=="":
            exit()
        z= graf in (emp['Ticker'].values)
        if z==False:
            print('No existe, prueba de nuevo ')
        else:
            periodo=int(input('Introduce Número de días a representar ,(0 = 10 años), Enter para salir: '))
            if periodo==0:
                periodo=3600
            td=timedelta(days=periodo)
            ta=th-td
            ta=ta.strftime('%Y-%m-%d')
            df=wb.DataReader(graf, 'yahoo',ta)['Close']
            fig= plt.figure(figsize=(20,10))
            ax1 = fig.add_subplot(111)
            ax1.set_title("Evolución precio " + str (graf)+ ' periodo: ' + str(periodo) + ' dias')
            ax1.set_ylabel("Precio")
            ax1.set_xlabel("Fechas")
            ax1.plot(df, color='b', lw=1)
            #ax1.legend()
            plt.show()
    except:
        break
exit()

Finalizamos el script añadiendo la funcionalidad de graficar el precio en función de la elección de ticker, siempre y cuando éste se encuentre en la columna.

Aquí tenéis el script entero:

import pandas_datareader.data as wb
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
import yfinance as yf

emp=pd.read_csv('Ibex.csv' ,header= None)
emp.rename(columns={0:'Nombre', 1:'Ticker'}, inplace=True)

print('\n','BOLSA IBEX 35\n','\n Ordenar por:','\n1.- Rentabilidad sobre cierre anterior','\n2.- Rentabilidad del día'
    ,'\n3.- Rentabilidad 52 semanas','\n4.- Capital Negociado (Open+Close)/2')

try:
    o=int(input('\nEscoge número para ordenación: '))
    if (o<0 or o>4):
        print('\nAlfabéticamente')
except:
    o=5 

tipo=str(input('\nAscendente o Descendente (A/D) ').upper())
if tipo!='A':
    tipo='D'

th=datetime.today()
td=timedelta(days=7)
td1=timedelta(days=365)

ta=th-td
ta1=th-td1-td
ta2=ta1+td

ta=ta.strftime('%Y-%m-%d')
ta1=ta1.strftime('%Y-%m-%d')
ta2=ta2.strftime('%Y-%m-%d')

tablat = pd.DataFrame()

for i in range(len(emp)):
    print(emp.iloc[i,0])

    tabla = yf.download(emp.iloc[i,1],ta,th, progress=False)[['Open', 'Close', 'Volume']]
    for w in range(len(tabla)-2):
        tabla = tabla.drop(tabla.index[0])

    ap = yf.download(emp.iloc[i,1],ta1, ta2, progress=False)[['Open','Close','Volume']]
    for w in range(len(ap)-2):
        ap = ap.drop(ap.index[0])
        ap

    tabla['% dia']=0
    tabla['% C. ant.']=0
    tabla['% 52 sem.']=0
    tabla['Capital(m)']=0
    tabla['Compañia']= emp.iloc[i,0]
    tabla['Ticker']= emp.iloc[i,1]
    tabla.iloc[1,0]= round(tabla.iloc[1,0],2)
    tabla.iloc[1,1]= round(tabla.iloc[1,1],2)
    tabla.iloc[1,3]= round(((tabla.iloc[1,1]/tabla.iloc[1,0])-1)*100,2)
    tabla.iloc[1,4]= round(((tabla.iloc[1,1]/tabla.iloc[0,1])-1)*100,2)
    tabla.iloc[1,5]= round(((tabla.iloc[1,1]/ap.iloc[1,1])-1)*100,2)
    tabla.iloc[1,6]= round((tabla.iloc[1,0]+tabla.iloc[1,1])*tabla.iloc[1,2]*0.0005,2)
    tabla=tabla.drop(['Volume'], axis=1)
    tabla=tabla.drop(tabla.index[0])
    tablat=pd.concat([tablat,tabla], axis=0)

tablat = tablat.set_index('Ticker')

if tipo=='D':
    if o==1:
        print(tablat.sort_values(by='% C. ant.', ascending=False))
    elif o==2:
        print(tablat.sort_values(by = '% dia', ascending=False))
    elif o==4:
        print(tablat.sort_values(by = 'Capital(m)', ascending=False))
    elif o==3:
            print(tablat.sort_values(by = '% 52 sem.', ascending=False))
    else:
        print(tablat.sort_values(by='Compañia', ascending=False))
elif tipo=='A':
    if o==1:
        print(tablat.sort_values(by='% C. ant.', ascending=True))
    elif o==2:
        print(tablat.sort_values(by = '% dia', ascending=True))
    elif o==4:
        print(tablat.sort_values(by = 'Capital(m)', ascending=True))
    elif o==3:
        print(tablat.sort_values(by = '% 52 sem.', ascending=True))
    else:
       print(tablat.sort_values(by='Compañia', ascending=True))

while True:
    try:
        graf=str(input('\nTicker a representar evolución: ').upper())
        if graf=="":
            exit()
        z= graf in (emp['Ticker'].values)
        if z==False:
            print('No existe, prueba de nuevo ')
        else:
            periodo=int(input('Introduce Número de días a representar ,(0 = 10 años), Enter para salir: '))
            if periodo==0:
                periodo=3600
            td=timedelta(days=periodo)
            ta=th-td
            ta=ta.strftime('%Y-%m-%d')
            df=wb.DataReader(graf, 'yahoo',ta)['Close']
            fig= plt.figure(figsize=(20,10))
            ax1 = fig.add_subplot(111)
            ax1.set_title("Evolución precio " + str (graf)+ ' periodo: ' + str(periodo) + ' dias')
            ax1.set_ylabel("Precio")
            ax1.set_xlabel("Fechas")
            ax1.plot(df, color='b', lw=1)
            #ax1.legend()
            plt.show()
    except:
        break
exit()

661 visitas
2    Login to like
Categorías:
 Estrategias   Estadísticas   Random   Gestión pasiva   Análisis técnico   Modelos   CEO   Mapas mentales   Liberalismo   Python   Growth   Niusleta   Ahorro   Recursos humanos   Inmobiliario   Fiscalidad   Value investing   Dividendos   Contabilidad   Marketing   Riesgo   IF   Cursos   Opciones   Bolsa