Trataremos de explicar un principio básico de las finanzas, la llamada frontera eficiente y sirve como una pequeña introducción en un área de las finanzas: “teoría de portafolio” usando R. Una segunda parte será entonces concentrarse en el capital-Asset-Pricing- método (CAPM) y sus supuestos, implicaciones e inconvenientes.

Introducción

Uno de los conceptos básicos de las finanzas es la relación riesgo-retorno. A modo de ejemplo ilustrativo: Si tuviera que ofrecer dos inversiones con el mismo riesgo pero con diferentes rendimientos esperados, tomaría la inversión con el rendimiento más alto (una mirada más atenta en lo que se entiende por los términos riesgo y rendimiento esperado sigue más adelante). Si las dos inversiones tuvieran el mismo rendimiento esperado pero con diferentes niveles de riesgo, la mayoría de la gente elige la inversión con menor riesgo. Este comportamiento se denomina la aversión al riesgo.

¿Pero qué sucede si tenemos que tomar la decisión entre una estrategia de cómo distribuir un valor de inversión de $ 100,000 entre las 30 empresas que figuran en el índice alemán DAX, las 100 empresas de FTSE de Gran Bretaña, las 500 empresas del S&P500, o la opción (más realista) para invertir en todas las acciones listadas en el mundo? ¿Cómo se distribuirá el dinero en efectivo para crear una cartera prometedora?

Esta es la pregunta Markowitz trató de responder en 1952 en su artículo “Portfolio Selection”. Ahora, muchos años después, muchos académicos y profesionales han añadido sus respuestas a la discusión, ampliando la discusión, pero la solución de Markowitz todavía se considera como una pieza fundamental en las finanzas. Su selección de la cartera permitió lo que ahora se llama “Capital Asset Pricing Model” o CAPM. Entonces nos preguntamos ¿qué hace el CAPM?, ¿cuáles son sus implicaciones, suposiciones?, ¿cuáles son sus inconvenientes?, y para nosotros lo más importante, ¿cómo podemos calcularlo en R?

Vamos a crear un ejemplo práctico para ilustrar la teoría: Comenzamos por la observación de tres diferentes acciones bien conocidas de Dow-Jones Industrial Average: IBM (IBM), Google/Alphabet (GOOG), y JP Morgan (JPM). He obtenido los datos mensuales desde el año 2000 a partir de Yahoo usando el libray quantmod. Podemos cargar los datos y trazar un índice de precios con el siguiente comando (Nota: usaremos data.table para el almacenamiento y manipulación de datos, pero también se puede utilizar dplyr, o base-r)

library(data.table)
library(scales)
library(ggplot2)
finanza<-fread("/Users/Victor/Documents/datos/datos_finanza.csv")
head(finanza)
##          date ticker    price
## 1: 2000-01-03    IBM 90.66385
## 2: 2000-02-01    IBM 87.95363
## 3: 2000-03-01    IBM 86.64813
## 4: 2000-04-03    IBM 88.68945
## 5: 2000-05-01    IBM 83.74264
## 6: 2000-06-01    IBM 89.13915
finanza[, date := as.Date(date)]
##            date ticker    price
##   1: 2000-01-03    IBM 90.66385
##   2: 2000-02-01    IBM 87.95363
##   3: 2000-03-01    IBM 86.64813
##   4: 2000-04-03    IBM 88.68945
##   5: 2000-05-01    IBM 83.74264
##  ---                           
## 532: 2016-01-04    JPM 58.04395
## 533: 2016-02-01    JPM 56.71510
## 534: 2016-03-01    JPM 59.04963
## 535: 2016-04-01    JPM 61.44905
## 536: 2016-05-02    JPM 61.84250
Creación de Indices de Valores
finanza[, id_price := price/price[1], by = ticker]
##            date ticker    price  id_price
##   1: 2000-01-03    IBM 90.66385 1.0000000
##   2: 2000-02-01    IBM 87.95363 0.9701069
##   3: 2000-03-01    IBM 86.64813 0.9557076
##   4: 2000-04-03    IBM 88.68945 0.9782228
##   5: 2000-05-01    IBM 83.74264 0.9236607
##  ---                                     
## 532: 2016-01-04    JPM 58.04395 1.9229179
## 533: 2016-02-01    JPM 56.71510 1.8788951
## 534: 2016-03-01    JPM 59.04963 1.9562350
## 535: 2016-04-01    JPM 61.44905 2.0357244
## 536: 2016-05-02    JPM 61.84250 2.0487589
Grafico de Indices de valores
ggplot(finanza, aes(x = date, y = id_price, color = ticker)) +
  geom_line() +
  theme_bw() + ggtitle("Evolución de los Precios") +
  xlab("Fecha") + ylab("Precio\n(Indexed 2000 = 1)") +
  scale_color_discrete(name = "Compañía")

Cálculo de la media Arimética
finanza[, ret := price / shift(price, 1) - 1, by = ticker]
##            date ticker    price  id_price          ret
##   1: 2000-01-03    IBM 90.66385 1.0000000           NA
##   2: 2000-02-01    IBM 87.95363 0.9701069 -0.029893096
##   3: 2000-03-01    IBM 86.64813 0.9557076 -0.014842993
##   4: 2000-04-03    IBM 88.68945 0.9782228  0.023558664
##   5: 2000-05-01    IBM 83.74264 0.9236607 -0.055776756
##  ---                                                  
## 532: 2016-01-04    JPM 58.04395 1.9229179 -0.110089722
## 533: 2016-02-01    JPM 56.71510 1.8788951 -0.022893787
## 534: 2016-03-01    JPM 59.04963 1.9562350  0.041162469
## 535: 2016-04-01    JPM 61.44905 2.0357244  0.040633842
## 536: 2016-05-02    JPM 61.84250 2.0487589  0.006402911
Tabla de Resumen no tomando valores NA
tab <- finanza[!is.na(ret), .(ticker, ret)]
head(tab)
##    ticker         ret
## 1:    IBM -0.02989310
## 2:    IBM -0.01484299
## 3:    IBM  0.02355866
## 4:    IBM -0.05577676
## 5:    IBM  0.06444169
## 6:    IBM -0.05886086
Cálculo de los retornos esperado (media histótica de los retornos) y la volatilidad (desviación estándar de los retornos)
tab <- tab[, .(er = round(mean(ret), 4),sd = round(sd(ret), 4)), by = "ticker"]
tab
##    ticker     er     sd
## 1:    IBM 0.0040 0.0554
## 2:   GOOG 0.0217 0.0801
## 3:    JPM 0.0064 0.0741
ggplot(tab, aes(x = sd, y = er, color = ticker)) +
  geom_point(size = 5) +
  theme_bw() + ggtitle("Riesgo Retorno") +
  xlab("Volatilidad") + ylab("Retornos Esperados") +
  scale_y_continuous(label = percent, limits = c(0, 0.03)) +
  scale_x_continuous(label = percent, limits = c(0, 0.1))

Frontera Eficiente

frontera<-data.table(read.csv("/Users/Victor/Documents/datos/mult_activos.csv"))
frontera
##                   x             y           z
##     1:  0.099276441  0.0332716757 0.054567527
##     2:  0.105473301  0.0406845529 0.053968795
##     3:  0.064534834  0.0040880569 0.049125643
##     4:  0.047325141  0.0549576911 0.051012931
##     5:  0.100294373  0.0102123152 0.042193865
##    ---                                       
##  9996:  0.139479551  0.0333821185 0.052897783
##  9997:  0.147881983  0.0411860741 0.057735664
##  9998:  0.111437836 -0.0009874475 0.004354541
##  9999: -0.009397453 -0.0054304977 0.047642833
## 10000:  0.070907275  0.0498671900 0.090684806

Cálculos de valores necesarios:

I) Retornos Esperados de las dos activos

RE_x <- mean(frontera$x)
RE_y <- mean(frontera$y)

II) Desviación Estándar (como medida de riesgo)

SD_x <- sd(frontera$x)
SD_y <- sd(frontera$y)

III) Covarianza

Cov_xy <- cov(frontera$x,frontera$y)

Creación de ponderaciones para 1000 portafolios

x_ponderacion <- seq(from = 0, to = 1, length.out = 1000)

Creación de un data.table que contiene las ponderaciones para los dos activos

dos_activos<- data.table(wx = x_ponderacion, wy = 1 - x_ponderacion)
dos_activos
##                wx          wy
##    1: 0.000000000 1.000000000
##    2: 0.001001001 0.998998999
##    3: 0.002002002 0.997997998
##    4: 0.003003003 0.996996997
##    5: 0.004004004 0.995995996
##   ---                        
##  996: 0.995995996 0.004004004
##  997: 0.996996997 0.003003003
##  998: 0.997997998 0.002002002
##  999: 0.998998999 0.001001001
## 1000: 1.000000000 0.000000000

Cálculo de los retornos esperados y las desviaciones estándar de los 1000 posibles portfolios

dos_activos[, ':=' (RE_p = wx*RE_x + wy*RE_y, SD_p = sqrt(wx^2*SD_x^2 + wy^2*SD_y^2 + 2*wx*(1 - wx)*Cov_xy))]
##                wx          wy       RE_p       SD_p
##    1: 0.000000000 1.000000000 0.03000000 0.01973497
##    2: 0.001001001 0.998998999 0.03004001 0.01971606
##    3: 0.002002002 0.997997998 0.03008001 0.01969728
##    4: 0.003003003 0.996996997 0.03012002 0.01967863
##    5: 0.004004004 0.995995996 0.03016002 0.01966010
##   ---                                              
##  996: 0.995995996 0.004004004 0.06980549 0.04979363
##  997: 0.996996997 0.003003003 0.06984550 0.04984333
##  998: 0.997997998 0.002002002 0.06988550 0.04989305
##  999: 0.998998999 0.001001001 0.06992551 0.04994277
## 1000: 1.000000000 0.000000000 0.06996551 0.04999250
head(dos_activos)
##             wx       wy       RE_p       SD_p
## 1: 0.000000000 1.000000 0.03000000 0.01973497
## 2: 0.001001001 0.998999 0.03004001 0.01971606
## 3: 0.002002002 0.997998 0.03008001 0.01969728
## 4: 0.003003003 0.996997 0.03012002 0.01967863
## 5: 0.004004004 0.995996 0.03016002 0.01966010
## 6: 0.005005005 0.994995 0.03020003 0.01964171

Trazo de los valores

ggplot() +
  geom_point(data = dos_activos, aes(x = SD_p, y = RE_p, color = wx)) +
  geom_point(data = data.table(sd = c(SD_x, SD_y), mean = c(RE_x, RE_y)),
  aes(x = sd, y = mean), color = "red", size = 3, shape = 18) +
  theme_bw() + ggtitle("Posibles Portfolios con dos activos de riesgo") +
  xlab("Volatilidad") + ylab("Retornos Esperados") +
  scale_y_continuous(label = percent, limits = c(0, max(dos_activos$RE_p) * 1.2)) +
  scale_x_continuous(label = percent, limits = c(0, max(dos_activos$SD_p) * 1.2)) +
  scale_color_continuous(name = expression(omega[x]), labels = percent)