Cómo elegir tu portafolio óptimo de inversión

El modelo de Markowitz y una implementación en Python

Cómo elegir tu portafolio óptimo de inversión

El primer paso es definir a lo que nos referimos con optimizar un portafolio de inversión. Para esto tenemos que reconocer que existe un tradeoff, un intercambio entre el riesgo y los retornos de inversión. También tenemos que recordar que existen diferentes perfiles de riesgo, pero en general vamos a adoptar un perfil de aversión al riesgo: para aceptar un nivel de riesgo mayor, los retornos deben de ser también mayores.

Por esta razón, como inversionistas nos interesa crear portafolios eficientes. Un portafolio eficiente es aquel que genera el menor nivel de riesgo para un cierto nivel de rendimiento y simultáneamente genera el mayor nivel de rendimientos para un cierto nivel de riesgo.

Llamamos a la colección de los portafolios eficientes la frontera eficiente. La frontera eficiente contiene un número infinito de portafolios eficientes y cada uno representa un nivel diferente de intercambio entre riesgo y rendimientos.

El modelo de Markowitz

En 1952 Harry Markowitz publicó un modelo matemático que, además de darle el pase a ser ganador de un premio Nobel, mostraba cómo distribuir un capital inicial en una colección de activos riesgosos para crear un portafolio eficiente. Es decir, uno con el menor riesgo dado un retorno esperado o que tenga el mayor retorno esperado dado un nivel de riesgo. En este post, mostraremos un poco de las matemáticas detrás de este modelo.

Comenzamos con un modelo sencillo. Asume que hoy t_0 usamos un capital inicial

para crear un portafolio distribuyendo el capital entre N diferentes activos que ya seleccionamos previamente. El modelo de Markowitz nos dice cómo distribuir el capital inicial entre diferentes activos. Podría ser, por ejemplo, que algunos de los activos reciban financiamiento cero, si el modelo así nos lo indica. En tal caso decimos que no tenemos posición con ese activo particular.

Supón ahora que el portafolio se debe de mantener por un periodo [t_0,t_f] en el que el portafolio no sufrirá ningún cambio.

Podemos entonces invertir una proporción w_i del capital inicial en el activo i. El total de las proporciones tiene que cumplir

Naturalmente,

Si S_i(t_0) es el precio del activo i en el momento inicial, entonces el monto de acciones que compramos de este activo es

El valor inicial del portafolio lo podemos entonces expresar por la siguiente igualdad

Un ejemplo en Python

Como primer paso, tenemos que extraer los datos. Para esto haremos uso del módulo pandas_datareader, que nos ayudará a extraer la información de Yahoo Finance de una manera extremadamente simple:

import pandas_datareader.data as getData 

Para extraer un activo, por ejemplo Apple:

aapl = getData.DataReader("AAPL", "yahoo") aapl.head() 

Esto nos muestra los precios de apertura, cierre, máximos y mínimos. Para nuestro ejemplo simplemente usaremos los precios de cierre de cada día. Podríamos usar la función drop() para eliminar las columnas que no estamos usando, o simplemente seleccionar la columna de precios de cierre con aapl['Close']. De hecho, vamos a generar una base de datos con varios precios de activos.

Para extraer múltiples activos por su ticker al mismo tiempo, usaremos el siguiente código

tickers = ["AAPL","VXRT", "BTC", "OSTK", "COIN"] assets = [getData.DataReader(ticker, "yahoo") for ticker in tickers] assets[0]["Close"].tail() 

En la primera línea de código simplemente asignamos una lista con los tickers de los que queremos extraer la información. La segunda línea asigna una lista con las bases de datos para todos los tickers. En la tercera línea simplemente estoy pidiendo que python me muestre los precios de cierre del primer activo de esta lista (recuerda que python comienza a contar desde el cero). En este caso, son los últimos precios de cierre de Apple.

Nuestro siguiente paso es extraer las columnas con los precios de cierre de cada uno de los activos y los ponga en una sola base de datos.

assets = [assets[asset_number]["Close"] for asset_number in range(0,len(assets))] 

stocks = pd.concat(assets, axis=1) 
stocks.columns = tickers 
stocks.tail() 

La primera línea genera una lista donde cada elemento es la base de datos únicamente con el precio de cierre de cada activo. En la segunda línea concatenamos la lista en una base de datos y en la tercera usamos la lista con los tickers para nombrar a las columnas. La última línea nos muestra el resultado de nuestra tabla.

Nuestro siguiente paso es normalizar los datos. Normalizar significa que todos los datos estén en la misma escala. Para lograrlo usaremos los retornos logarítmicos: el logaritmo de los retornos diarios.

import numpy as np 

log_ret = np.log(stocks/stocks.shift(1))
log_ret.tail() # Esta línea muestra la base de datos con los retornos 

¡Ahora viene lo más interesante! Haremos un ciclo for para generar diferentes combinaciones de las acciones y guardaremos el ratio de Sharpe. El tamaño de la operación depende de la velocidad de procesamiento con el que cuentes. Por ejemplo, unas 10,000 combinaciones parece un buen número. Tu puedes hacerlo con menos si este cálculo es demasiado pesado para tu computadora.

np.random.seed(42) 
n_portfolios = 10000 
all_weights = np.zeros((n_portfolios, len(stocks.columns))) retornos = np.zeros(n_portfolios)
volatilidades = np.zeros(n_portfolios) 
sharpe = np.zeros(n_portfolios)  

for portafolio in range(n_portfolios):
   # Calcular las proporciones   
   weights = np.array(np.random.random(5)) 
   
   # genera un portafolio con proporciones aleatorias   
   weights = weights/np.sum(weights)    

   # Incluimos el peso de este portafolio en el conjunto de portafolios   
   all_weights[portafolio,:] = weights    
   # ¿Cuál es el retorno esperado de este portafolio
   retornos[portafolio] = np.sum((log_ret.mean() * weights         *252))  
  
   # ¿Cuál es la volatilidad esperada?
   volatilidades[portafolio] = np.sqrt(np.dot(weights.T, np.dot(log_ret.cov()*252, weights))) 
   
   # Ratio de Sharpe   
   sharpe[portafolio] = retornos[portafolio]/volatilidades[portafolio] 

En la primera parte de este código asignamos los vectores con retornos y volatilidades con ceros. Después comenzamos a generar las simulaciones. Para imprimir los resultados podemos usar

print('El mayor ratio de Sharpe es : {}'.format(sharpe.max())) print('Está ubicado en la posición {}'.format(sharpe.argmax())) 

Para identificar el portafolio 2498 que tiene el mayor ratio de Sharpe usamos

print(all_weights[2498,:]) 

# Además guardamos los retornos y volatilidad máximos para usarlos más adelante 

maximo_retorno = retornos[sharpe.argmax()] maxima_volatilidad = volatilidades[sharpe.argmax()] 

¡Listo! Estos resultados nos indican que tenemos que dedicar un asombroso 57% de nuestro portafolio a ¡BITCOIN! Esto es por el tamaño gigantesco del rendimiento en los últimos días y meses. Interesante, ¿no? Sin embargo, de acuerdo a la teoría, hay un rango completo de portafolios que son eficientes. Esta frontera de portafolios eficientes la podemos visualizar usando el siguiente código.

import matplotlib.pyplot as plt 

plt.figure(figsize = (12,8)) 
plt.scatter(volatilidades, retornos, c =sharpe, cmap = 'viridis') 
plt.colorbar(label = "Ratio de Sharpe") plt.xlabel('Volatilidad') plt.ylabel('Retornos') plt.scatter(maxima_volatilidad, maximo_retorno, c = 'red', s = 50) 
plt.show() 

Este ejemplo muestra un conjunto de portafolios con una forma de bala muy pronunciada. En la punta de la bala se encuentra nuestro portafolio óptimo. La frontera de portafolios eficientes puede alcanzarse a visualizar, pero vamos a crearla. Para esto necesitamos algunas funciones adicionales. La primera toma las ponderaciones y para cada ponderación regresa los retornos, la volatilidad y el ratio de Sharpe. La segunda nos ayuda a obtener el ratio de Sharpe negativo para algunas ponderaciones . La tercera sirve para verificar que la suma de las ponderaciones sea cero. Esto lo hace regresando 0 si esta condición se cumple.


¡Genial! Te has registrado exitosamente.

¡Bienvenido de vuelta! Has iniciado sesión correctamente.

Te has suscrito correctamente a Marionomics: Escribe tu paper de economía.

¡Éxito! Revisa tu correo electrónico para obtener el enlace mágico para iniciar sesión.

¡Éxito! Se ha actualizado la información de facturación.

No se actualizó tu información de facturación.