Notas de repaso sobre


Inicialización, descarga de precios.

Se comienza con especificar algunas funciones complementarias como limpiar el environment (Seguramente correrás este código más de una vez), la forma de expresar una cifra (Sin notación científica), cargar las librerías, y los datos necesarias para descargar los precios de QUANDL.

# Remover todos los objetos del "Environment"
rm(list = ls())

# los 0s aceptados antes de expresas una cifra en notación científica
options("scipen"=100, "digits"=4)

### Cargas librerías a utilizar
suppressMessages(library(plotly)) # Graficas interactivas
suppressMessages(library(Quandl)) # Descargar Precios
suppressMessages(library(PortfolioAnalytics)) # Teoría Moderna de Portafolios
suppressMessages(library(ROI)) # Optimizacion para portafolio

suppressMessages(library(knitr))  # Opciones de documentación + código
suppressMessages(library(kableExtra)) # Tablas en HTML
options(knitr.table.format = "html") 

# Cargar el token de QUANDL
Quandl.api_key("ZsvuyVCEdbf6zrg7t5Dy")

# Funcion para descagar precios
Bajar_Precios <- function(Columns, Tickers, Fecha_In, Fecha_Fn) {
  
  # Funcion para descargar N cantidad de activos desde QUANDL
  # -- Dependencias: QUANDL
  # -- Columns : columnas a incluir : character : c("date", "adj_close", ... )
  # -- Tickers : Tickers o claves de pizarra de los activos : character : "TSLA"
  # -- Fecha_In : Fecha Inicial : character : "2017-01-02"
  # -- Fecha_Fn : Fecha Final : character : "2017-08-02"
  
  # Peticion para descargar precios
  Datos <- Quandl.datatable(code = "WIKI/PRICES", qopts.columns=Columns, ticker=Tickers,
                            date.gte=Fecha_In, date.lte=Fecha_Fn)
  return(Datos)
}

# Tickers de accciones y datos a solicitar a QUANDL
tk <- c("TSLA", "BBY", "HD")
cs <- c("date", "adj_close")

# Fecha inicial y fecha final
fs <- c("2015-08-01", "2017-08-01")

# Capital inicial a considerar
Capital_Inicial <- 100000
Comision <- 0.005
# Descargar Precios y Calcular rendimientos
Datos <- list()

for(i in 1:length(tk))
  Datos[[i]] <- Bajar_Precios(Columns=cs, Ticker=tk[i], Fecha_In=fs[1], Fecha_Fn=fs[2])

names(Datos) <- tk


Calcular los rendimientos logarítmicos.

Utilizaremos la propiedad de los logarítmos para calcular los rendimientos.

for(i in 1:length(tk))
  Datos[[i]]$adj_close_r <- c(0, diff(log(Datos[[i]]$adj_close)))


Transformación a XTS

Las funciones a utilizar de la librería PortfolioAnalytics pedirán que los datos de entrada sean del tipo XTS.

Rends <- xts(x = cbind(Datos[[1]]$adj_close_r, Datos[[2]]$adj_close_r, Datos[[3]]$adj_close_r),
             order.by = Datos[[1]]$date)[-1]
names(Rends) <- tk


Crear un objeto tipo portfolio

Para hacerlo simplemente crea un Objeto tipo “Portfolio”, especificando el nombre de los activos que contendrá.

Port1 <- portfolio.spec(assets=tk)


Especificar restricciones del portafolio

En este caso especificaremos dos restricciones: Restriccion 1: La suma de todos los pesos debe de ser 1, es decir, Invertir el 100% del capital en el portafolio.

Port1 <- add.constraint(portfolio=Port1,
                        type="full_investment")

# Restriccion 2: Limites superior e inferior para el valor de los pesos individuales
Port1 <- add.constraint(portfolio=Port1,
                        type="box", 
                        min=c(0.01, 0.01, 0.01), max=c(0.7, 0.7, 0.7))


Especificar los objetivos del portafolio

En este caso especificamos el objetivo de encontrar el portafolio con máximo rendimiento.

# Restricción 3: Objetivo de rendimiento
Port1 <- add.objective(portfolio=Port1, type="return", name="mean")


Optimizar portafolio

Existen distintos métodos de optimización ofrecidos por éste paquete, utilzaremos el más simple, generar números aleatorios según el criterio interno y la búsqueda para cuando se alcance el criterio de paro, que es alcanzar un objetivo previamente declarado, en este caso el de un rendimiento esperado máximo despues de n iteraciones de portafolios aleatorios.

# Optimizacion de portafolio con PSO
Port1 <- optimize.portfolio(R=Rends, portfolio=Port1, optimize_method="random",
                                  trace=TRUE, search_size=5000)


Visualizar portafolios

Vamos a construir las funciones con las cuales almacenaremos los datos que nos interesan de los portafolio encontrados, que son: Los pesos, los rendimientos esperados (Medias), el riesgo (Varianzas), así como distinguir cuales de estos portafolios se considera están dentro o en una vecindad de la frontera eficiente.

Portafolios <- vector("list", length = length(Port1$random_portfolio_objective_results))

for(i in 1:length(Port1$random_portfolio_objective_results)) {
  Portafolios[[i]]$Pesos  <- Port1$random_portfolio_objective_results[[i]]$weights
  Portafolios[[i]]$Medias <- Port1$random_portfolio_objective_results[[i]]$objective_measures$mean
  Portafolios[[i]]$Vars   <- var.portfolio(R = Port1$R, weights = Portafolios[[i]]$Pesos)
  names(Portafolios[[i]]$Medias) <- NULL
}

df_Portafolios <- data.frame(matrix(nrow=length(Port1$random_portfolio_objective_results),
                                    ncol=3, data = 0))
colnames(df_Portafolios) <- c("Rend","Var","Clase")

for(i in 1:length(Port1$random_portfolio_objective_results)) {
  
  df_Portafolios$Rend[i] <- round(Portafolios[[i]]$Medias*252,4)
  df_Portafolios$Var[i]  <- round(sqrt(Portafolios[[i]]$Vars)*sqrt(252),4)
  df_Portafolios$Clase[i] <- "No-Frontera"
  
  for(k in 1:length(tk)) {
    df_Portafolios[i,paste("Peso_", tk[k],sep="")] <- Portafolios[[i]]$Pesos[k]

    df_Portafolios[i,paste("Titulos_ini_", tk[k],sep="")] <-
      (Capital_Inicial*Portafolios[[i]]$Pesos[k])%/%Datos[[k]]$adj_close[1]
  }
  
  
}

# Titulos <- (Capital_Inicial*Pesos[1,1])%/%Datos[[1]]$adj_close[1]


Plot_portafolios <- plot_ly(x=df_Portafolios$Var, y=df_Portafolios$Rend, type='scatter', mode='markers',
                            name = "Portafolios", marker = list(color="grey", size=7), hoverinfo='text', 
                    text = ~paste('Rendimiento: ', paste(df_Portafolios$Rend*100, "%") ,
                                  '<br> Riesgo: ', paste(df_Portafolios$Var*100, "%") )) %>% 
                    layout(title = "Portafolios (Markowitz)",
                           xaxis = list(title = "Riesgo (Desviación Estándar Anualizada)",
                                        showgrid = F),
                           yaxis = list(title = "Valor Esperado (Rendimiento Anualizado)"),
                           legend = list(orientation = 'h', y = -0.25))
Plot_portafolios


Gráfica histórica de portafolios

Seguramente habrá un gran número de portafolios generados, para cada uno se puede calcular, por ejemplo, el valor de su postura inicial, el valor final y obtener el rendimiento final después del periodo. También, por ejemplo, se puede calcular para cada periodo (dato) el valor del portafolio, asumiendo que los pesos permanecen constantes en el tiempo, y con eso graficar el histórico de ese valo de portafolio para cada tiempo. Pero la cuestión es, qué portafolios estaría interesante de graficar. Bueno, te propongo los 3 “Más básicos”: El del máximo rendimiento, el de la mínima varianza y el del máximo sharpe ratio.

# Portafolio con máximo rendimiento esperado
Port_1 <- df_Portafolios[which.max(df_Portafolios$Rend),]

# Portafolio con mínima varianza
Port_2 <- df_Portafolios[which.min(df_Portafolios$Var),]

# Tasa libre de riesgo
rf <- 0.0025          
# Rendimiento de portafolio
rp <- df_Portafolios$Rend
# Varianza de portafolio
sp <- df_Portafolios$Var
# Indice de sharpe
sharpe <- (rp-rf)/sp

# Portafolio con máximo Sharpe ratio 
Port_3 <- df_Portafolios[which.max(sharpe),]

Ports <- cbind(rbind(Port_1, Port_2, Port_3),
               "Portafolio" = c("Máximo Rendimiento","Mínima Varianza","Máximo Sharpe Ratio"))
> kable(Ports) %>%
+   kable_styling(bootstrap_options = c("striped", "hover","condensed", "responsive"))
Rend Var Clase Peso_TSLA Titulos_ini_TSLA Peso_BBY Titulos_ini_BBY Peso_HD Titulos_ini_HD Portafolio
372 0.2857 0.2804 No-Frontera 0.018 6 0.698 2347 0.284 253 Máximo Rendimiento
228 0.1716 0.1804 No-Frontera 0.142 54 0.158 531 0.700 624 Mínima Varianza
165 0.2235 0.2075 No-Frontera 0.012 4 0.388 1304 0.600 535 Máximo Sharpe Ratio


Distinguiendo los nuevos portafolios

Si quisieramos volver a graficar la nube de dispersión de puntos y localizar los portafolios elegidos en el código anterior, pregunta: ¿ Todos los portafolios estarían sobre, o al menos cerca, de la frontera eficiente ? , veamos.

Plot_portafolios <- plot_ly(x=df_Portafolios$Var, y=df_Portafolios$Rend, type='scatter', mode='markers',
                            name = "Portafolios", marker = list(color="grey", size=7), hoverinfo='text', 
                    text = ~paste('Rendimiento: ', paste(df_Portafolios$Rend*100, "%") ,
                                  '<br> Riesgo: ', paste(df_Portafolios$Var*100, "%") )) %>% 
                    layout(title = "Portafolios (Markowitz)",
                           xaxis = list(title = "Riesgo (Desviación Estándar Anualizada)",
                                        showgrid = F),
                           yaxis = list(title = "Valor Esperado (Rendimiento Anualizado)"),
                           legend = list(orientation = 'h', y = -0.25)) %>%
                    add_trace(x = ~Ports$Var[1], y = ~Ports$Rend[1], name = Ports$Portafolio[1],
                              mode = 'marker', marker = list(color="red", size=10)) %>%
                    add_trace(x = ~Ports$Var[2], y = ~Ports$Rend[2], name = Ports$Portafolio[2],
                              mode = 'marker', marker = list(color="blue", size=10)) %>%
                    add_trace(x = ~Ports$Var[3], y = ~Ports$Rend[3], name = Ports$Portafolio[3],
                              mode = 'marker', marker = list(color="orange", size=10))
Plot_portafolios


La evolución del valor en los portafolios: El cáculo

Podemos entonces, para cada portafolio (en este ejemplo analizamos 3), calcular la evolución histórica de su balance.

# Pesos y titulos iniciales, de todos los activos, para los 3 portafolios
Pesos_Titulos <- Ports[,-c(1,2,3)]

# Encontrar las columnas cuyo nombre contenga "Titulos_ini", con esas encontraremos más fácil los títulos
# por portafolio por activo
Ind <- grep(pattern = "Titulos_ini",x = colnames(Pesos_Titulos))
Historicos_Ports <- data.frame("Date" = Datos[[1]]$date)

# Crear data frame que contendrá los datos finales de cada estrategia
for(i in 1:length(Ports[,1])) {
  Historicos_Ports[[paste("Portafolio_",i,sep="")]] <- 
    (Datos[[1]]$adj_close*Pesos_Titulos[i,Ind[1]]  + 
     Datos[[2]]$adj_close*Pesos_Titulos[i,Ind[2]] +
     Datos[[3]]$adj_close*Pesos_Titulos[i,Ind[3]])
}


La evolución del valor en los portafolios: La gráfica

También podemos visualizar el comportamiento histórico.

plot_ly(Historicos_Ports) %>%
 add_trace(x = ~Date, y = ~round(Portafolio_1,2), type = 'scatter', mode = 'lines', name = 'Máximo Rendimiento',
            line = list(color = 'red'), hoverinfo = "text", text = ~paste('Port_1',round(Portafolio_1,2))) %>%
  add_trace(x = ~Date, y = ~round(Portafolio_2,2), type = 'scatter', mode = 'lines', name = 'Mínima Varianza',
            line = list(color = 'blue'), hoverinfo = "text", text = ~paste('Port_2',round(Portafolio_2,2)))  %>%
  add_trace(x = ~Date, y = ~round(Portafolio_3,2), type = 'scatter', mode = 'lines', name = 'Máximo Sharpe Ratio',
            line = list(color = 'orange'), hoverinfo = "text", text = ~paste('Port_3',round(Portafolio_3,2)))%>% 
  layout(title = "3 Portafolios distintos objetivos",
         xaxis = list(title = "Fechas", showgrid = T),
         yaxis = list(title = "Balance"), 
         legend = list(orientation = 'h', y = -0.25, x = 0.5))


Algunas conclusiones

En la primera cuarta parte de las fechas se puede observar que el portafolio con mínima varianza obtuvo un rendimiento igual e incluso superior, en algunos periodos pequeños, que el portafolio de máximo rendimiento, sin embargo no fue mayor que el portafolio de máximo sharpe ratio salvo un par de fechas en concreto. Es visiblemente claro también, sobre todo en las últimas fechas, que la variabilidad vertical de la serie de tiempo, que en realidad representaría la varianza de los resultados, si es notoriamente mayor en el portafolio de máximo rendimiento, cuando el de mínima varianza muestra una misma tendencia alcista pero con reducida volatilidad. Finalmente podremos notar que el portafolio de máximo sharpe ratio puede ser considerado como “el término medio”, su gráifca histórica mostró un rendimiento intermedio entre los otros dos portafolios, de igual manera su volatilidad, con esto comprobamos (al menos visualmente y para sólo este caso) que utilizar el criterio de máximo sharpe ratio para la elección de un conjunto de activos nos expondrá a una evolución del balance del portafolio menos volatil que los portafolios que nos dan más rendimientos pero también con mayor rendimiento que los portafolios con menor varianza, es decir, un portafolio eficiente en comparación con el costo de oportunidad financiero y balance entre riesgo-rendimiento.