"Un chimpancé lanzando dardos sobre una página de cotizaciones sería capaz de seleccionar una cartera más rentable que la mayoría".
Quien supera al mercado, ¿lo supera porque realiza correctamente la selección de empresas y el momento en el que invertir o es porque estadísticamente le ha tocado?
En este artículo vamos a replicar el experimento que trató de refutar la crítica del norteamericano Burton Gordon Malkiel en 1973, en el que afirmaba que un mono lanzando dardos sería capaz de tener unos retornos similares al resto de gestores.
La idea es generar aleatoriamente 12 carteras de 10 activos, ponderadas por igual, y calcular la rentabilidad media para el periodo.
Veamos el código:
Vamos a necesitar random
para la selección al azar y probablemente consigamos mayor eficiencia utilizando yfinance
para la descarga de precios que con pandas_datareader
.
import pandas as pd
import random
import yahoo_fin.stock_info as si
import matplotlib.pyplot as plt
import yfinance as yf
Creamos las variables a utilizar y un DataFrame llamado monos que va a contener nuestras 12 carteras con 10 activos con sus respectivos retornos y la rentabilidad media.
tickers=[]
tabla=[]
ticks=[0]*10
barras=[]
monos=pd.DataFrame(columns=['Random 1','Random 2','Random 3',
'Random 4','Random 5','Random 6','Random 7',
'Random 8','Random 9','Random 10','Rent.md.'], index=range(24))
Con yahoo_fin.stock_info
utilizo el método tickers_sp500()
para tener todos mis activos en una lista.
lt = si.tickers_sp500()
Básicamente he decidido crear un DataFrame de 24 filas y tener cada ticker con su rentabilidad. En la variable ticks utilizamos el método random.sample
, para seleccionar 10 números sobre una lista de 500, y luego con otro bucle hacemos 10 iteraciones para añadir los 10 tickers a los que corresponden esos números y descargamos los precios de cierre para el periodo considerado.
for m in range(0,24,2):
print('Mono ',m//2)
ticks=random.sample(range(len(lt)),10)
for i in range(10):
tickers.append(lt[ticks[i]])
df = yf.download(tickers,auto_adjust=False,start="2021-01-01")['Close']
A continuación rellenamos una variable con append
con el cálculo de los retornos de cada activo y completamos el DataFrame de monos de forma intercalada, partiendo del bucle for m in range(0,24,2)
, y reiniciamos nuestras variables (las vaciamos) para iniciar otra recursión.
Finalmente una fila con tickers y otra con los retornos, únicamente nos quedará calcular la rentabilidad media.
for i in range(10):
tabla.append(round(float(df.iloc[(len(df)-1),i]/df.iloc[0,i]-1)*100,2))
monos.iloc[m,i]=tickers[i]
monos.iloc[(m+1),i]=tabla[i]
tickers=[]
tabla=[]
Siguiendo este esquema de bucles, calculamos la suma de los retornos de los 10 activos de cada cartera y luego dividimos entre 10, ya que cada activo tiene el mismo peso, lo pasamos a una lista barras que luego graficaremos, y rellenamos esa columna al DataFrame de monos
suma=float(0)
for i in range(0,24,2):
for n in range(10):
suma=suma+float(monos.iloc[(i+1),n])
suma=round(float(suma)/10,2)
barras.append(suma)
monos.iloc[i+1,10]=suma
monos.iloc[(i),10]=('Mono: ',i//2+1)
suma=0
Finalmente nos queda graficar esa columna con la rentabilidad media.
Genero un índice para el gráfico con "Monos x" y grafico barras que es donde he pasado mi variable suma con las rentabilidades medias.
monos2 = []
for x in range(12):
monos2.append("Mono/s "+str(x+1))
plt.figure(figsize=(16,8))
plt.bar(monos2, barras)
plt.title('Rentabilidad de la cartera de cada mono')
plt.ylabel('Porcentaje %')
plt.xlabel('Monos')
plt.show()
Aquí tenéis el código completo:
import pandas as pd
import random
import yahoo_fin.stock_info as si
import matplotlib.pyplot as plt
import yfinance as yf
tickers=[]
tabla=[]
ticks=[0]*10
barras=[]
monos=pd.DataFrame(columns=['Random 1','Random 2','Random 3',
'Random 4','Random 5','Random 6','Random 7',
'Random 8','Random 9','Random 10','Rent.md.'], index=range(24))
lt = si.tickers_sp500()
for m in range(0,24,2):
print('Mono ',m//2)
ticks=random.sample(range(len(lt)),10)
for i in range(10):
tickers.append(lt[ticks[i]])
df = yf.download(tickers,auto_adjust=False,start="2021-01-01")['Close']
for i in range(10):
tabla.append(round(float(df.iloc[(len(df)-1),i]/df.iloc[0,i]-1)*100,2))
monos.iloc[m,i]=tickers[i]
monos.iloc[(m+1),i]=tabla[i]
tickers=[]
tabla=[]
suma=float(0)
for i in range(0,24,2):
for n in range(10):
suma=suma+float(monos.iloc[(i+1),n])
suma=round(float(suma)/10,2)
barras.append(suma)
monos.iloc[i+1,10]=suma
monos.iloc[(i),10]=('Mono: ',i//2+1)
suma=0
print(monos)
monos2 = []
for x in range(12):
monos2.append("Mono/s "+str(x+1))
plt.figure(figsize=(16,8))
plt.bar(monos2, barras)
plt.title('Rentabilidad de la cartera de cada mono')
plt.ylabel('Porcentaje %')
plt.xlabel('Monos')
plt.show()