Notas de repaso sobre
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
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)))
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
Para hacerlo simplemente crea un Objeto tipo “Portfolio”, especificando el nombre de los activos que contendrá.
Port1 <- portfolio.spec(assets=tk)
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))
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")
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)
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
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 |
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
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]])
}
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))
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.