Introduccíon a R para Finanzas

Clase 0: Técnico en Finanzas Cuantitativas

Author

Prof: Máster Esteban Sandoval L.

Published

March 22, 2025

Introducción

Que es R?

R frente a Python

  • La sintaxis de R, Python, Matlab y Julia no es tan diferente Ver comparativo.

  • R y Python son los lenguajes más populares para la ciencia de datos. Algunas comparaciones fueron realizadas por Norm Matloff, Arslan Shahid, Martijn Theuwissen.

  • Visualización de datos: Tanto R como Python cuentan con numerosas bibliotecas de gráficos, pero el paquete R ggplot2 (basado en el concepto de “gramática de gráficos”) es el claro ganador (véase la galería. Python también cuenta con una biblioteca ggplot.

  • Bibliotecas de modelado: Tanto R como Python cuentan con numerosas bibliotecas. Parece que R tiene más potencial en ciencia de datos. En estadística, R es el claro ganador.

  • Facilidad de aprendizaje: Python se diseñó en 1989 para programadores con una sintaxis inspirada en C. R se desarrolló alrededor de 1993 para estadísticos y científicos, también con una sintaxis inspirada en C. Algunos piensan que Python es más fácil, mientras que otros piensan que R lo es. Quizás inicialmente R era más difícil de aprender que Python, pero con los entornos de desarrollo integrado (IDE) modernos como RStudio, esto ya no es así. Algunos podrían decir que Python es más elegante, pero eso depende de a qué esté acostumbrado cada uno.

  • Velocidad: Inicialmente, Python era más rápido que R. Sin embargo, con los paquetes modernos existentes, esto ya no es así. Por ejemplo, el famoso paquete de R data.table para manipular grandes cantidades de datos es el claro ganador (véase benchmark). De hecho, el paquete de R Rcpp permite combinar R con C++, lo que resulta en implementaciones muy rápidas de los paquetes. Recientemente, R también se ha beneficiado de muchos paquetes de computación paralela.

  • Soporte de la comunidad: Ambos lenguajes cuentan con una base de usuarios significativa, por lo que ambos cuentan con una comunidad de soporte muy activa.

  • Aprendizaje automático: Python es más popular para redes neuronales. Sin embargo, lo cierto es que las bibliotecas de aprendizaje profundo más populares (p. ej., TensorFlow, Keras,H2O, etc.) están codificadas en C y tienen interfaces con Python, R y otros lenguajes. Curiosamente, los bosques aleatorios (uno de los métodos de aprendizaje automático más populares) son muy superiores en R. Esto se debe a que las redes neuronales tradicionalmente provienen de la informática, mientras que los bosques aleatorios provienen de la estadística.

  • ¿Por qué R?: R se ha utilizado para computación estadística durante más de dos décadas. Puedes empezar a escribir código útil enseguida. Ha sido ampliamente utilizado por científicos de datos y cuenta con una enorme cantidad de paquetes disponibles para diversas tareas relacionadas con la ciencia de datos.

  • ¿Por qué Python?: Python es un lenguaje de programación de propósito general. Para aplicaciones web, Python parece ser más popular.

  • Finanzas: De nuevo, tanto R como Python se utilizan ampliamente en finanzas. Es fácil encontrar defensores y detractores apasionados de cada lenguaje. Según mis propias observaciones en los sectores académico e industrial, puedo afirmar que R es imbatible para pruebas rápidas y desarrollo de prototipos, y quizás Python se utilice más en etapas posteriores, cuando el producto final (probablemente web) debe desarrollarse para los clientes.

Instalación

Para instalarlo, simplemente siga estos sencillos pasos:

  1. Instale R desde CRAN, acá una Guía rápida

  2. Instale el IDE gratuito RStudio, acá una Guía rápida

Ahora está listo para empezar a usar R desde RStudio (tenga en cuenta que también puede usar R directamente desde la línea de comando sin RStudio o usar otro IDE de su preferencia).

Paquetes

Para ver las versiones de R y los paquetes instalados, simplemente escriba sessionInfo():

sessionInfo()
R version 4.4.2 (2024-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 10 x64 (build 19045)

Matrix products: default


locale:
[1] LC_COLLATE=English_United States.utf8 
[2] LC_CTYPE=English_United States.utf8   
[3] LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C                          
[5] LC_TIME=English_United States.utf8    

time zone: America/Guatemala
tzcode source: internal

attached base packages:
[1] parallel  stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
 [1] PCRA_1.2                   data.table_1.17.2         
 [3] CVXR_1.0-15                PortfolioAnalytics_2.1.0  
 [5] foreach_1.5.2              plotly_4.10.4             
 [7] viridisLite_0.4.2          reshape2_1.4.4            
 [9] rugarch_1.5-3              ggrepel_0.9.6             
[11] scales_1.4.0               prophet_1.0               
[13] rlang_1.1.6                Rcpp_1.0.14               
[15] forecast_8.24.0            PerformanceAnalytics_2.0.8
[17] quantmod_0.4.27            TTR_0.24.4                
[19] xts_0.14.1                 tidyquant_1.0.11          
[21] fGarch_4033.92             AER_1.2-14                
[23] survival_3.8-3             sandwich_3.1-1            
[25] lmtest_0.9-40              car_3.1-3                 
[27] carData_3.0-5              ismev_1.42                
[29] mgcv_1.9-3                 nlme_3.1-168              
[31] ghyp_1.6.5                 MASS_7.3-65               
[33] numDeriv_2016.8-1.1        pastecs_1.4.2             
[35] tseries_0.10-58            zoo_1.8-14                
[37] evir_1.7-4                 FRAPO_0.4-1               
[39] timeSeries_4041.111        timeDate_4041.110         
[41] Rglpk_0.6-5.1              slam_0.1-55               
[43] cccp_0.3-1                 portfolioBacktest_0.4.1   
[45] fBasics_4041.97            gganimate_1.0.9           
[47] lubridate_1.9.4            forcats_1.0.0             
[49] stringr_1.5.1              dplyr_1.1.4               
[51] purrr_1.0.4                readr_2.1.5               
[53] tibble_3.2.1               ggplot2_3.5.2             
[55] tidyverse_2.0.0            gifski_1.32.0-2           
[57] tidyr_1.3.1               

loaded via a namespace (and not attached):
 [1] RColorBrewer_1.1-3          rstudioapi_0.17.1          
 [3] jsonlite_2.0.0              cvar_0.5                   
 [5] magrittr_2.0.3              farver_2.1.2               
 [7] nloptr_2.2.1                rmarkdown_2.29             
 [9] ROI.plugin.symphony_1.0-0   vctrs_0.6.5                
[11] htmltools_0.5.8.1           progress_1.2.3             
[13] curl_6.2.3                  truncnorm_1.0-9            
[15] Formula_1.2-5               GenSA_1.1.14.1             
[17] pracma_2.4.4                KernSmooth_2.23-26         
[19] htmlwidgets_1.6.4           plyr_1.8.9                 
[21] lifecycle_1.0.4             iterators_1.0.14           
[23] pkgconfig_2.0.3             Matrix_1.7-3               
[25] R6_2.6.1                    fastmap_1.2.0              
[27] rbibutils_2.3               digest_0.6.37              
[29] colorspace_2.1-1            spatial_7.3-18             
[31] GeneralizedHyperbolic_0.8-7 timechange_0.3.0           
[33] httr_1.4.7                  abind_1.4-8                
[35] compiler_4.4.2              bit64_4.6.0-1              
[37] withr_3.0.2                 backports_1.5.0            
[39] R.utils_2.13.0              corpcor_1.6.10             
[41] tools_4.4.2                 RobStatTM_1.0.11           
[43] nnet_7.3-20                 R.oo_1.27.1                
[45] glue_1.8.0                  quadprog_1.5-8             
[47] R.cache_0.17.0              grid_4.4.2                 
[49] checkmate_2.3.2             generics_0.1.4             
[51] pso_1.0.4                   gtable_0.3.6               
[53] tzdb_0.5.0                  R.methodsS3_1.8.2          
[55] hms_1.1.3                   pillar_1.10.2              
[57] robustbase_0.99-5           Rsymphony_0.1-33           
[59] splines_4.4.2               tweenr_2.0.3               
[61] lattice_0.22-7              bit_4.6.0                  
[63] gmp_0.7-5                   ROI_1.0-1                  
[65] ks_1.15.1                   tidyselect_1.2.1           
[67] registry_0.5-1              SkewHyperbolic_0.4-2       
[69] pbapply_1.7-2               knitr_1.50                 
[71] urca_1.3-4                  xfun_0.52                  
[73] DEoptimR_1.1-3-1            Rsolnp_1.16                
[75] stringi_1.8.7               lazyeval_0.2.2             
[77] yaml_2.3.10                 boot_1.3-31                
[79] evaluate_1.0.3              codetools_0.2-20           
[81] DistributionUtils_0.6-2     cli_3.6.5                  
[83] RcppParallel_5.1.10         spd_2.0-1                  
[85] Rdpack_2.6.4                gbutils_0.5                
[87] mco_1.17                    fracdiff_1.5-3             
[89] prettyunits_1.2.0           mclust_6.1.1               
[91] Rmpfr_1.1-0                 mvtnorm_1.3-3              
[93] crayon_1.5.3               

Para ver la versión de un paquete específico, usa packageVersion("package_name").

Con el tiempo, tendrás que instalar diferentes paquetes desde CRAN con el comando install.packages("package_name") o desde GitHub con el comando devtools::install_github("package_name").

Después de instalar un paquete, es necesario cargarlo antes de poder usarlo con el comando library("package_name") o library(package_name).

# Intentemos usar la función xts() del paquete xts:
x <- xts()
#> Error en xts(): No se pudo encontrar la función "xts"

# Intentemos cargar el paquete primero:
library(xts)
#> Error en library(xts): No existe el paquete "xts"

# Instalamos primero:
install.packages("xts")

# Ahora podemos cargarlo y usarlo:
library(xts)
x <- xts()

Variables y tipos de datos

En R, podemos asignar fácilmente un valor a una variable u objeto con <- (si la variable no existe, se creará):

x <- "Hola"
x
[1] "Hola"

Podemos combinar varios elementos con c():

y <- c("Hola", "todos")
y
[1] "Hola"  "todos"

Siempre podemos ver las variables en memoria con ls():

ls()
[1] "x" "y"

Mi comando favorito es str(variable). Proporciona información diversa sobre la variable, como su tipo, dimensiones, contenido, etc.

str(x)
 chr "Hola"
str(y)
 chr [1:2] "Hola" "todos"

Otro par de comandos útiles son head() y tail(). Son especialmente útiles para variables de gran tamaño, ya que muestran los primeros y los últimos elementos, respectivamente.

x <- c(1:1000)
str(x)
 int [1:1000] 1 2 3 4 5 6 7 8 9 10 ...
head(x)
[1] 1 2 3 4 5 6
tail(x) 
[1]  995  996  997  998  999 1000

Es importante destacar que R es un lenguaje funcional donde casi todo se hace mediante funciones de todo tipo (como str(), print(), head(), ls(), tail(), max(), etc.).

x <- c(1:1000)
str(x)
 int [1:1000] 1 2 3 4 5 6 7 8 9 10 ...
head(x)
[1] 1 2 3 4 5 6
tail(x) 
[1]  995  996  997  998  999 1000

Hay una variedad de funciones para obtener ayuda:

help(matrix) # Ayuda sobre la función matrix()
?matrix # Lo mismo
example(matrix) # Mostrar un ejemplo de la función matrix()
apropos("matrix") # Listar todas las funciones que contienen la cadena "matrix"

# Obtener viñetas de los paquetes instalados
vignette() # Mostrar las viñetas disponibles
vignette("xts") # Mostrar una viñeta específica

Operadores en R: Los operadores aritméticos incluyen +, -, *, /, ^ y los operadores lógicos >, >=, ==, !=.

R ofrece una amplia variedad de tipos de datos, incluyendo escalares, vectores, matrices, marcos de datos y listas.

Vectores

Un vector es simplemente una colección de varias variables del mismo tipo (numéricas, de caracteres, lógicas, etc.).

a <- c(1, 2, 5.3, 6, -2, 4)  # vector numérico
b <- c("one", "two", "three")  # vector de caracteres
c <- c(TRUE, TRUE, TRUE, FALSE, TRUE, FALSE) # vector lógico

Haga referencia a los elementos de un vector utilizando sub índices:

a[2]  # 2do elemento del vector
[1] 2
a[c(2, 4)]  # 2do y 4to elemento del vector
[1] 2 6

Tenga en cuenta que en R los vectores no son vectores columna ni vectores fila; no tienen orientación. Si se desea un vector columna, este es en realidad una matriz.

También es importante diferenciar la multiplicación elemento por elemento * del producto interno %*% y el producto externo %o%:

x <- c(1, 2)
y <- c(10, 20)
x * y
[1] 10 40
x %*% y
     [,1]
[1,]   50
t(x) %*% y
     [,1]
[1,]   50
x %o% y
     [,1] [,2]
[1,]   10   20
[2,]   20   40

Los elementos de un vector se pueden nombrar así:

names(y)
NULL
names(y) <- c("convex", "optimization")
y  # lo mismo que imprimir (y)
      convex optimization 
          10           20 
str(y) # ver la estructura de y
 Named num [1:2] 10 20
 - attr(*, "names")= chr [1:2] "convex" "optimization"
length(y) # podemos obtener la longitud
[1] 2

Matrices

Una matriz es un conjunto bidimensional de varias variables del mismo tipo (numéricas, de caracteres, lógicas, etc.).

Podemos crear fácilmente una matriz con matrix():

# generar matriz numérica de 5 x 4
x <- matrix(1:20, nrow = 5, ncol = 4)
x
     [,1] [,2] [,3] [,4]
[1,]    1    6   11   16
[2,]    2    7   12   17
[3,]    3    8   13   18
[4,]    4    9   14   19
[5,]    5   10   15   20
#podemos nombrar las columnas y las filas
colnames(x) <- c("col1", "col2", "col3", "col4")
rownames(x) <- c("row1", "row2", "row3", "row4", "row5")
x
     col1 col2 col3 col4
row1    1    6   11   16
row2    2    7   12   17
row3    3    8   13   18
row4    4    9   14   19
row5    5   10   15   20
str(x) # ver la estructura de x
 int [1:5, 1:4] 1 2 3 4 5 6 7 8 9 10 ...
 - attr(*, "dimnames")=List of 2
  ..$ : chr [1:5] "row1" "row2" "row3" "row4" ...
  ..$ : chr [1:4] "col1" "col2" "col3" "col4"
dim(x) # podemos obtener las dimensiones o el número de filas/columnas
[1] 5 4
nrow(x) # podemos obtener el número de filas
[1] 5
ncol(x) # podemos obtener el número de columnas
[1] 4

Identificar filas, columnas o elementos utilizando sub índices:

x[, 4] # 4ta columna de la matriz (devuelta como vector)
row1 row2 row3 row4 row5 
  16   17   18   19   20 
str(x[, 4])
 Named int [1:5] 16 17 18 19 20
 - attr(*, "names")= chr [1:5] "row1" "row2" "row3" "row4" ...
x[, 4, drop = FALSE] # 4ta columna de la matriz (devuelta como matriz de una columna)
     col4
row1   16
row2   17
row3   18
row4   19
row5   20
str(x[, 4, drop = FALSE])
 int [1:5, 1] 16 17 18 19 20
 - attr(*, "dimnames")=List of 2
  ..$ : chr [1:5] "row1" "row2" "row3" "row4" ...
  ..$ : chr "col4"
x[3, ] # 3ra fila de la matriz
col1 col2 col3 col4 
   3    8   13   18 
x[2:4, 1:3] # filas 2,3,4 de columnas 1,2,3
     col1 col2 col3
row2    2    7   12
row3    3    8   13
row4    4    9   14
str(x[2:4, 1:3])
 int [1:3, 1:3] 2 3 4 7 8 9 12 13 14
 - attr(*, "dimnames")=List of 2
  ..$ : chr [1:3] "row2" "row3" "row4"
  ..$ : chr [1:3] "col1" "col2" "col3"

Marcos de datos

Un marco de datos es más general que una matriz, ya que las diferentes columnas pueden tener distintos modos (numérico, de caracteres, factorial, etc.).

d <- c(1, 2, 3, 4)
e <- c("red", "white", "red", NA)
f <- c(TRUE, TRUE, TRUE, FALSE)
datos <- data.frame(d, e, f)
names(datos) <- c("ID", "Color", "Passed") # nombres de variables
datos
  ID Color Passed
1  1   red   TRUE
2  2 white   TRUE
3  3   red   TRUE
4  4  <NA>  FALSE
str(datos)
'data.frame':   4 obs. of  3 variables:
 $ ID    : num  1 2 3 4
 $ Color : chr  "red" "white" "red" NA
 $ Passed: logi  TRUE TRUE TRUE FALSE

Hay varias formas de identificar los elementos de un marco de datos:

datos[c(1, 3)] # columnas 1,3 del marco de datos
  ID Passed
1  1   TRUE
2  2   TRUE
3  3   TRUE
4  4  FALSE
datos[c("ID", "Color")] # columnas ID y Color del marco de datos
  ID Color
1  1   red
2  2 white
3  3   red
4  4  <NA>
datos["ID"] # seleccionar ID de columna
  ID
1  1
2  2
3  3
4  4
datos$ID # extrae el ID de la variable en el marco de datos (como un vector), como datos[["ID"]]
[1] 1 2 3 4

Los marcos de datos en R son muy potentes y versátiles. Se utilizan comúnmente en aprendizaje automático, donde cada fila representa una observación y cada columna una variable (cada variable puede ser de diferentes tipos).

En aplicaciones financieras, trabajamos principalmente con series temporales multivariadas, que pueden considerarse como una matriz o un marco de datos, pero con algunas particularidades: cada fila representa una observación, pero en un orden específico (indexada correctamente con fechas u horas) y cada columna corresponde al mismo tiempo (numérico). Para series temporales multivariadas, exploraremos más adelante la clase xts, que es más apropiada que las matrices o los marcos de datos.

Listas

Una lista es una colección ordenada de objetos (componentes) de (posiblemente) diferentes tipos. Una lista permite agrupar diversos objetos (posiblemente no relacionados) bajo un mismo nombre.

# Una lista con 4 componentes: una cadena, un vector numérico, una matriz y un escalar
w <- list(name = "Esteban", numeros = c(1:10), matriz = matrix(NA, 3, 3), edad = 30)
w
$name
[1] "Esteban"

$numeros
 [1]  1  2  3  4  5  6  7  8  9 10

$matriz
     [,1] [,2] [,3]
[1,]   NA   NA   NA
[2,]   NA   NA   NA
[3,]   NA   NA   NA

$edad
[1] 30
str(w) 
List of 4
 $ name   : chr "Esteban"
 $ numeros: int [1:10] 1 2 3 4 5 6 7 8 9 10
 $ matriz : logi [1:3, 1:3] NA NA NA NA NA NA ...
 $ edad   : num 30

Funciones útiles

length(object) # número de elementos o componentes de un vector
str(object) # estructura de un objeto
class(object) # clase o tipo de un objeto
names(object) # nombres

c(object, object, ...) # combinar objetos en un vector
cbind(object, object, ...) # combinar objetos como columnas
rbind(object, object, ...) # combinar objetos como filas

object # imprimir el objeto
print(object) # imprimir el objeto

ls() # listar los objetos actuales
rm(object) # eliminar un objeto

Gráficos

Base de R

Las funciones de gráficos base/nativas en R son bastante simples y pueden no ser tan atractivas como se desea:

set.seed(1)
x <- rnorm(1000)  # generar números aleatorios normales
x <- cumsum(x)    # paseo aleatorio
plot(x, type = "l", col = "blue", main = "Paseo aleatorio", xlab = "Tiempo", ylab = "Precio logarítmico")
lines(cumsum(rnorm(1000)), col = "red")

Para múltiples gráficos:

par(mfrow = c(2, 2))  # define una matriz de gráficos 2x2
plot(cumsum(rnorm(1000)), type = "l", ylab = "x1")
plot(cumsum(rnorm(1000)), type = "l", ylab = "x2")
plot(cumsum(rnorm(1000)), type = "l", ylab = "x3")
plot(cumsum(rnorm(1000)), type = "l", ylab = "x4")

par(mfrow = c(1, 1))  # establece el gráfico único predeterminado

Hay dos paquetes muy recomendados para realizar gráficos: ggplot2 y Plotly.

ggplot2

El paquete ggplot2 es extremadamente popular en la comunidad de R. Aquí se encuentra la página web oficial, el libro en línea gratuito ggplot2: Gráficos elegantes para el análisis de datos y una guía práctica. Es particularmente versátil y está adaptado a los marcos de datos. Se basa en el concepto de “gramática de gráficos”.

# Simular datos
set.seed(123)  # para reproducibilidad
df <- data.frame(
  index   = 1:1000,
  series1 = cumsum(rnorm(1000)),
  series2 = cumsum(rnorm(1000)),
  series3 = cumsum(rnorm(1000)),
  series4 = cumsum(rnorm(1000))
)

# Aplicar melt correctamente desde reshape2
molten_df <- reshape2::melt(
  df,
  id.vars = "index",
  measure.vars = c("series1", "series2", "series3", "series4")
)

# Ver estructura
str(molten_df)
'data.frame':   4000 obs. of  3 variables:
 $ index   : int  1 2 3 4 5 6 7 8 9 10 ...
 $ variable: Factor w/ 4 levels "series1","series2",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ value   : num  -0.56 -0.791 0.768 0.839 0.968 ...
ggplot(molten_df, aes(x = index, y = value, col = variable)) + 
  geom_line() +
  ggtitle("Paseo aleatorio")

ggplot(molten_df, aes(x = index, y = value, col = variable)) + 
  geom_line(show.legend = FALSE) +
  facet_wrap(~ variable) +
  ggtitle("Paseo aleatorio")

Plotly

El paquete Plotly crea gráficos interactivos con calidad de publicación. Ejemplos de cómo crear gráficos de líneas, de dispersión, de áreas, de barras, de barras de error, de caja, histogramas, mapas de calor, subgráficos, ejes múltiples y gráficos 3D (basados en WebGL).

set.seed(123)
y1 <- cumsum(rnorm(1000))
y2 <- cumsum(rnorm(1000))
y3 <- cumsum(rnorm(1000))
x <- 1:1000
plot_ly() %>%
  add_lines(x = x, y = y1, name = "line 1", line = list(color = "blue", width = 2)) %>%
  add_lines(x = x, y = y2, name = "line 2", line = list(color = "green", width = 2)) %>%
  add_lines(x = x, y = y3, name = "line 3", line = list(color = "purple", width = 2)) %>%
  layout(
    title = "Paseos aleatorios",
    xaxis = list(title = "T"),
    yaxis = list(title = "Precio logarítmico"),
    legend = list(x = 1, y = 1, xanchor = "right", yanchor = "top"))

Paquetes clave para finanzas

Paquete xts

Como se mencionó anteriormente, en finanzas trabajamos principalmente con series temporales multivariadas que pueden considerarse matrices donde cada fila es una observación en un orden específico (indexada correctamente con fechas u horas) y todas las columnas son del mismo tiempo (numérico) correspondiente a diferentes activos. Se podría simplemente usar un objeto de la clase matrix o data.frame. Sin embargo, existe una clase muy práctica (del paquete xts) diseñada específicamente para ese propósito: xts (de hecho, es la culminación de una larga trayectoria de desarrollo de otras clases como ts, fts, mts, irts, tseries, timeSeries y zoo).

Creación de objetos xts

Se pueden convertir fácilmente los datos de una serie temporal existente en xts con as.xts():

library(xts)

data(sample_matrix)  # carga algunos datos del paquete xts
class(sample_matrix)
[1] "matrix" "array" 
str(sample_matrix)
 num [1:180, 1:4] 50 50.2 50.4 50.4 50.2 ...
 - attr(*, "dimnames")=List of 2
  ..$ : chr [1:180] "2007-01-02" "2007-01-03" "2007-01-04" "2007-01-05" ...
  ..$ : chr [1:4] "Open" "High" "Low" "Close"
matrix_xts <- as.xts(sample_matrix, dateFormat = "Date")
class(matrix_xts)
[1] "xts" "zoo"
str(matrix_xts)
An xts object on 2007-01-02 / 2007-06-30 containing: 
  Data:    double [180, 4]
  Columns: Open, High, Low, Close
  Index:   Date [180] (TZ: "UTC")

Alternativamente, se pueden crear nuevos datos xts con el constructor xts():

xts(1:10, as.Date("2000-01-01") + 1:10)
           [,1]
2000-01-02    1
2000-01-03    2
2000-01-04    3
2000-01-05    4
2000-01-06    5
2000-01-07    6
2000-01-08    7
2000-01-09    8
2000-01-10    9
2000-01-11   10

Subconjuntos xts

La diferencia más notable en el comportamiento de los objetos xts se evidencia en el uso del operador “[”. Mediante una notación especial, se pueden usar cadenas de tipo fecha para extraer datos según el índice de tiempo. Al aumentar el nivel de detalle temporal, es posible crear subconjuntos del objeto por año, semana, días o incluso segundos.

El argumento i (fila) del operador de subconjunto “[”, además de aceptar valores numéricos para la indexación, también puede ser una cadena de caracteres, un objeto basado en el tiempo o un vector de ambos. El formato debe especificarse a la izquierda con respecto al formato de tiempo estándar ISO:8601 “AAAA-MM-DD HH:MM:SS”. Esto significa que para extraer un mes en particular, también es necesario especificar el año completo.

Para identificar una hora en particular, por ejemplo, todas las observaciones en la octava hora del 1 de enero de 2007, también se necesitaría incluir el año, mes y día completos; por ejemplo, “2007-01-01 08”. También es posible solicitar explícitamente un rango de horas mediante este subconjunto basado en índices, utilizando el separador de rangos recomendado por ISO, “/”. La forma básica es “desde/hasta”, donde tanto “desde” como “hasta” son opcionales. Si falta alguno de los lados, se interpreta como una solicitud para recuperar datos desde el principio o hasta el final del objeto de datos.

Otra ventaja de este método es que las horas exactas de inicio y fin no tienen por qué coincidir con los datos subyacentes: se devolverá la observación disponible más cercana dentro del período solicitado.

El siguiente ejemplo muestra cómo extraer el mes completo de marzo de 2007:

matrix_xts["2007-03"]
               Open     High      Low    Close
2007-03-01 50.81620 50.81620 50.56451 50.57075
2007-03-02 50.60980 50.72061 50.50808 50.61559
2007-03-03 50.73241 50.73241 50.40929 50.41033
2007-03-04 50.39273 50.40881 50.24922 50.32636
2007-03-05 50.26501 50.34050 50.26501 50.29567
2007-03-06 50.27464 50.32019 50.16380 50.16380
2007-03-07 50.14458 50.20278 49.91381 49.91381
2007-03-08 49.93149 50.00364 49.84893 49.91839
2007-03-09 49.92377 49.92377 49.74242 49.80712
2007-03-10 49.79370 49.88984 49.70385 49.88698
2007-03-11 49.83062 49.88295 49.76031 49.78806
2007-03-12 49.82763 49.90311 49.67049 49.74033
2007-03-13 49.69628 49.70863 49.37924 49.37924
2007-03-14 49.36270 49.53735 49.30746 49.53735
2007-03-15 49.57374 49.62310 49.39876 49.49600
2007-03-16 49.44900 49.65285 49.42416 49.59500
2007-03-17 49.55666 49.55666 49.33564 49.34714
2007-03-18 49.29778 49.67857 49.29778 49.65463
2007-03-19 49.62747 49.65407 49.51604 49.54590
2007-03-20 49.59529 49.62003 49.42321 49.50690
2007-03-21 49.49765 49.53961 49.41610 49.51807
2007-03-22 49.42306 49.42306 49.31184 49.39687
2007-03-23 49.27281 49.27281 48.93095 48.93095
2007-03-24 48.86635 48.86635 48.52684 48.52684
2007-03-25 48.50649 48.50649 48.33409 48.33973
2007-03-26 48.34210 48.44637 48.28969 48.28969
2007-03-27 48.25248 48.41572 48.23648 48.30851
2007-03-28 48.33090 48.53595 48.33090 48.53595
2007-03-29 48.59236 48.69988 48.57432 48.69988
2007-03-30 48.74562 49.00218 48.74562 48.93546
2007-03-31 48.95616 49.09728 48.95616 48.97490

Ahora extraiga todos los datos desde el principio hasta el 7 de enero de 2007:

matrix_xts["/2007-01-07"] # igual que: matrix_xts["::2007-01-07"]
               Open     High      Low    Close
2007-01-02 50.03978 50.11778 49.95041 50.11778
2007-01-03 50.23050 50.42188 50.23050 50.39767
2007-01-04 50.42096 50.42096 50.26414 50.33236
2007-01-05 50.37347 50.37347 50.22103 50.33459
2007-01-06 50.24433 50.24433 50.11121 50.18112
2007-01-07 50.13211 50.21561 49.99185 49.99185

Otras herramientas xts que permiten la creación de subconjuntos son las funciones first y last. Siguiendo el ejemplo de head y tail del paquete recomendado utils, permiten la creación de subconjuntos basados en cadenas, sin obligar al usuario a ajustarse a las especificaciones del índice de tiempo. Aquí se muestra la primera semana de los datos:

first(matrix_xts,"1 week")
               Open     High      Low    Close
2007-01-02 50.03978 50.11778 49.95041 50.11778
2007-01-03 50.23050 50.42188 50.23050 50.39767
2007-01-04 50.42096 50.42096 50.26414 50.33236
2007-01-05 50.37347 50.37347 50.22103 50.33459
2007-01-06 50.24433 50.24433 50.11121 50.18112
2007-01-07 50.13211 50.21561 49.99185 49.99185

…y aquí están los primeros 3 días de la última semana de datos.

first(last(matrix_xts,"1 week"),"3 days")
               Open     High      Low    Close
2007-06-25 47.20471 47.42772 47.13405 47.42772
2007-06-26 47.44300 47.61611 47.44300 47.61611
2007-06-27 47.62323 47.71673 47.60015 47.62769

Si bien la capacidad de subconjuntos mencionada anteriormente hace que la clase temporal elegida para el índice sea menos relevante, es un factor que conviene controlar.

Para ello, xts ofrece funciones para indexar según cualquiera de las clases temporales actuales. Estas incluyen Date, POSIXct, chron, yearmon, yearqtr y timeDate. Se puede acceder al índice mediante los genéricos zoo extendidos a xts: index y la función de reemplazo index<-.

También es posible consultar y establecer directamente la clase de índice de un objeto xts mediante las funciones tclass y tclass<-, respectivamente.

La conversión temporal, que genera un nuevo objeto con la clase de índice solicitada, se puede realizar mediante la función convertIndex.

tclass(matrix_xts)
[1] "Date"
matrix_xts_POSIX <- convertIndex(matrix_xts,'POSIXct')
tclass(matrix_xts_POSIX)
[1] "POSIXct" "POSIXt" 

Por supuesto, también se puede utilizar la indexación tradicional para matrices:

matrix_xts[1:5] # igual que matrix_xts[1:5, ]
               Open     High      Low    Close
2007-01-02 50.03978 50.11778 49.95041 50.11778
2007-01-03 50.23050 50.42188 50.23050 50.39767
2007-01-04 50.42096 50.42096 50.26414 50.33236
2007-01-05 50.37347 50.37347 50.22103 50.33459
2007-01-06 50.24433 50.24433 50.11121 50.18112
matrix_xts[1:5, 4]
              Close
2007-01-02 50.11778
2007-01-03 50.39767
2007-01-04 50.33236
2007-01-05 50.33459
2007-01-06 50.18112
matrix_xts[1:5, "Close"]
              Close
2007-01-02 50.11778
2007-01-03 50.39767
2007-01-04 50.33236
2007-01-05 50.33459
2007-01-06 50.18112
matrix_xts[1:5]$Close
              Close
2007-01-02 50.11778
2007-01-03 50.39767
2007-01-04 50.33236
2007-01-05 50.33459
2007-01-06 50.18112

Finalmente, es sencillo combinar diferentes objetos xts en uno con múltiples columnas y alineados correctamente por el índice de tiempo con merge() o simplemente el más estándar cbind() (que llama a merge()):

open_close <- cbind(matrix_xts$Open, matrix_xts$Close)
str(open_close)
An xts object on 2007-01-02 / 2007-06-30 containing: 
  Data:    double [180, 2]
  Columns: Open, Close
  Index:   Date [180] (TZ: "UTC")

Herramientas adicionales

Calcular periodicidad: La función periodicity proporciona un resumen rápido de la periodicidad subyacente de los objetos de series temporales:

periodicity(matrix_xts)
Daily periodicity from 2007-01-02 to 2007-06-30 

Encontrar puntos finales por tiempo: Otro problema común con los datos de series temporales es la identificación de los puntos finales con respecto al tiempo. A menudo es necesario dividir los datos en intervalos horarios o mensuales para calcular alguna estadística. Una simple llamada a endpoints ofrece un vector rápido de valores adecuado para crear subconjuntos de un conjunto de datos. Tenga en cuenta que el primer elemento es cero, que se utiliza para delimitar el final.

endpoints(matrix_xts, on = "months")
[1]   0  30  58  89 119 150 180
matrix_xts[endpoints(matrix_xts, on = "months")]
               Open     High      Low    Close
2007-01-31 50.07049 50.22578 50.07049 50.22578
2007-02-28 50.69435 50.77091 50.59881 50.77091
2007-03-31 48.95616 49.09728 48.95616 48.97490
2007-04-30 49.13825 49.33974 49.11500 49.33974
2007-05-31 47.82845 47.84044 47.73780 47.73780
2007-06-30 47.67468 47.94127 47.67468 47.76719
endpoints(matrix_xts, on = "weeks")
 [1]   0   6  13  20  27  34  41  48  55  62  69  76  83  90  97 104 111 118 125
[20] 132 139 146 153 160 167 174 180
head(matrix_xts[endpoints(matrix_xts, on = "weeks")])
               Open     High      Low    Close
2007-01-07 50.13211 50.21561 49.99185 49.99185
2007-01-14 50.46359 50.62395 50.46359 50.60145
2007-01-21 50.16188 50.42090 50.16044 50.42090
2007-01-28 49.96586 50.00217 49.87468 49.88096
2007-02-04 50.48183 50.55509 50.40203 50.55509
2007-02-11 50.67849 50.91776 50.67849 50.91160

Cambiar la periodicidad: Uno de los tipos de datos más comunes en finanzas son los datos OHLC (Apertura-Máximo-Mínimo-Cierre). A menudo es necesario cambiar la periodicidad de estos datos a algo más burdo, por ejemplo, tomar datos diarios y agregarlos a semanales o mensuales. Con to.period y las funciones contenedoras relacionadas, es una propuesta sencilla.

to.period(matrix_xts, "months")
           matrix_xts.Open matrix_xts.High matrix_xts.Low matrix_xts.Close
2007-01-31        50.03978        50.77336       49.76308         50.22578
2007-02-28        50.22448        51.32342       50.19101         50.77091
2007-03-31        50.81620        50.81620       48.23648         48.97490
2007-04-30        48.94407        50.33781       48.80962         49.33974
2007-05-31        49.34572        49.69097       47.51796         47.73780
2007-06-30        47.74432        47.94127       47.09144         47.76719
periodicity(to.period(matrix_xts, "months"))
Monthly periodicity from 2007-01-31 to 2007-06-30 
# cambiando el índice a algo más apropiado
to.monthly(matrix_xts)
         matrix_xts.Open matrix_xts.High matrix_xts.Low matrix_xts.Close
Jan 2007        50.03978        50.77336       49.76308         50.22578
Feb 2007        50.22448        51.32342       50.19101         50.77091
Mar 2007        50.81620        50.81620       48.23648         48.97490
Apr 2007        48.94407        50.33781       48.80962         49.33974
May 2007        49.34572        49.69097       47.51796         47.73780
Jun 2007        47.74432        47.94127       47.09144         47.76719

Aplicar una función periódicamente: A menudo es deseable poder calcular una estadística específica o evaluar una función en un conjunto de períodos de tiempo no superpuestos. Con la familia de funciones period.apply es bastante sencillo. Los siguientes ejemplos ilustran una aplicación sencilla de la función max a nuestros datos de ejemplo:

# la función general, llama internamente a sapply
period.apply(matrix_xts[, "Close"], INDEX = endpoints(matrix_xts), FUN = max)
              Close
2007-01-31 50.67835
2007-02-28 51.17899
2007-03-31 50.61559
2007-04-30 50.32556
2007-05-31 49.58677
2007-06-30 47.76719
# Mismo resultado que el anterior, solo una interfaz mensual
apply.monthly(matrix_xts[, "Close"], FUN = max)
              Close
2007-01-31 50.67835
2007-02-28 51.17899
2007-03-31 50.61559
2007-04-30 50.32556
2007-05-31 49.58677
2007-06-30 47.76719
# usando una de las funciones optimizadas - aproximadamente 4 veces más rápido
period.max(matrix_xts[,4], endpoints(matrix_xts))
               [,1]
2007-01-31 50.67835
2007-02-28 51.17899
2007-03-31 50.61559
2007-04-30 50.32556
2007-05-31 49.58677
2007-06-30 47.76719

Además de apply.monthly, existen envoltorios para otros intervalos de tiempo comunes, como apply.daily, apply.weekly, apply.quarterly y apply.yearly. Las funciones optimizadas actuales incluyen period.max, period.min, period.sum y period.prod.

Paquete quantmod

El paquete quantmod está diseñado para ayudar al operador cuantitativo en el desarrollo, prueba e implementación de modelos de trading basados en estadísticas.

Obtención de datos: La función más útil de quantmod es getSymbol(), que permite cargar fácilmente datos desde diversos sitios web como YahooFinance, GoogleFinance, FRED, etc.:

library(quantmod)
getSymbols(c("AAPL", "BLK"), from = "2022-01-01", to = "2025-04-30", src = "yahoo")
[1] "AAPL" "BLK" 
str(AAPL)
An xts object on 2022-01-03 / 2025-04-29 containing: 
  Data:    double [833, 6]
  Columns: AAPL.Open, AAPL.High, AAPL.Low, AAPL.Close, AAPL.Volume ... with 1 more column
  Index:   Date [833] (TZ: "UTC")
  xts Attributes:
    $ src    : chr "yahoo"
    $ updated: POSIXct[1:1], format: "2025-06-05 23:37:00"
head(BLK)
           BLK.Open BLK.High BLK.Low BLK.Close BLK.Volume BLK.Adjusted
2022-01-03   918.34   922.07  905.43    911.73     470800     832.5643
2022-01-04   917.72   927.48  916.56    917.22     495800     837.5778
2022-01-05   918.15   925.50  890.83    891.77     573300     814.3375
2022-01-06   893.36   898.18  884.03    895.49     637000     817.7345
2022-01-07   895.27   896.63  884.00    892.27     643300     814.7941
2022-01-10   888.70   889.87  862.84    873.34     788500     797.5079

Conceptos básicos OHLCV: Los datos suelen incluir los precios de apertura, máximo, mínimo, cierre, cierre ajustado y volumen. Existen numerosas funciones útiles para extraer estos datos, como Op(), Hi(), Lo(), Cl(), Ad(), Vo(), así como para realizar diversas consultas como is.OHLC(), has.Vo(), etc.

getSymbols("GS") # Goldman OHLC de Yahoo
[1] "GS"
is.OHLC(GS) # ¿Los datos contienen al menos OHL y C?
[1] TRUE
has.Vo(GS) # ¿Y el volumen?
[1] TRUE
head(Op(GS))  # solo la columna Open
           GS.Open
2007-01-03  200.60
2007-01-04  200.22
2007-01-05  198.43
2007-01-08  199.05
2007-01-09  203.54
2007-01-10  203.40

Gráficos con quantmod: La función chartSeries() es una herramienta útil para visualizar series temporales financieras de una forma familiar para muchos profesionales: gráficos de líneas, así como gráficos de barras y velas OHLC.

Existen envoltorios prácticos para estos diferentes estilos (lineChart(), barChart() y candleChart()), aunque chartSeries() contribuye significativamente a gestionar automáticamente los datos de la forma más adecuada.

chartSeries(AAPL["2024-8/2024-12"], name = "AAPL")

# Añadir multicolor y cambiar el fondo a blanco
candleChart(AAPL["2024-8/2024-12"], name = "AAPL", multi.col = TRUE, theme = "white")

# Ahora semanalmente con velas de colores personalizados usando la función quantmod to.weekly
chartSeries(to.weekly(AAPL), up.col = "white", dn.col = "blue", name = "AAPL")

Herramientas de gráficos de análisis técnico: Se pueden añadir estudios de análisis técnico del paquete TTR a los gráficos anteriores:

Seguidamente, podemos estimar con parámetros muy sencillos el cálculo una media móvil simple y media móvil exponencial, con las funciones SMA() y EMA(), respectivamente.

candleChart(AAPL["2024-8/2024-12"], name = "AAPL", multi.col = TRUE, theme = "white", TA="addVo();addSMA(n = 20, col = 'red'); addEMA(n = 50, col = 'blue')")

chartSeries(AAPL["2024"], name = "AAPL",
            TA = "addMACD(); addBBands()")

chartSeries(AAPL["2024"], name = "AAPL", 
            TA = "addMomentum(); addEMA(); addRSI()")

chartSeries(AAPL["2024"], theme = "white", type = "candles", name = "AAPL", 
            TA = "addMomentum(); addEMA(); addRSI()")

Paquete TTR

El paquete TTR (Reglas Técnicas de Trading) está diseñado para el análisis técnico tradicional y la creación de gráficos.

Medias Móviles: Se pueden calcular fácilmente las medias móviles (https://www.rdocumentation.org/packages/TTR/versions/0.24.4/topics/SMA)

SMA.10 <- SMA(Cl(AAPL), n = 5)
head(SMA.10, 10)
               SMA
2022-01-03      NA
2022-01-04      NA
2022-01-05      NA
2022-01-06      NA
2022-01-07 176.160
2022-01-10 174.196
2022-01-11 173.272
2022-01-12 173.394
2022-01-13 173.432
2022-01-14 173.612
EMA.30 <- EMA(Cl(AAPL), n = 30)
plot(cbind(Cl(AAPL), EMA.30), legend.loc = "topleft", main = "Media Móvil Exponencial de 30 días")

Bandas de Bollinger: Las bandas de Bollinger son una técnica muy popular. Muchos traders creen que cuanto más se acercan los precios a la banda superior, más sobrecomprado está el mercado, y cuanto más se acercan los precios a la banda inferior, más sobrevendido es el mercado. John Bollinger tiene un conjunto de 22 reglas a seguir al usar las bandas como sistema de negociación.

bb20 <- BBands(HLC(AAPL), sd = 2)
str(bb20)
An xts object on 2022-01-03 / 2025-04-29 containing: 
  Data:    double [833, 4]
  Columns: dn, mavg, up, pctB
  Index:   Date [833] (TZ: "UTC")
  xts Attributes:
    $ src    : chr "yahoo"
    $ updated: POSIXct[1:1], format: "2025-06-05 23:37:00"
plot(bb20, main = "Bandas Bollinger de 2 desviaciones estándar")

Indicador de fuerza relativa (RSI): Este indicador es un oscilador que mide la velocidad y el cambio de los movimientos del precio. Su popularidad se debe a su capacidad para identificar condiciones de sobrecompra y sobreventa en el mercado, ofreciendo señales valiosas para la toma de decisiones en trading.

RSI <- RSI(Cl(AAPL), n = 14)
plot(cbind(Cl(AAPL), RSI), legend.loc = "topleft", main = "RSI de 14 días")

Paquete PerformanceAnalytics

El paquete PerformanceAnalytics contiene una amplia lista de funciones útiles para la evaluación del rendimiento y la visualización de los resultados.

retornos <- CalculateReturns(cbind(Cl(AAPL), Cl(BLK)))[-1, ]
head(retornos )
              AAPL.Close    BLK.Close
2022-01-04 -0.0126915973  0.006021509
2022-01-05 -0.0265998824 -0.027746835
2022-01-06 -0.0166933352  0.004171446
2022-01-07  0.0009883614 -0.003595764
2022-01-10  0.0001161891 -0.021215543
2022-01-11  0.0167837816  0.017003659
table.AnnualizedReturns(retornos)
                          AAPL.Close BLK.Close
Annualized Return             0.0461    0.0029
Annualized Std Dev            0.2956    0.2815
Annualized Sharpe (Rf=0%)     0.1560    0.0103
table.CalendarReturns(retornos)
      Jan  Feb  Mar  Apr  May  Jun Jul  Aug  Sep  Oct Nov  Dec AAPL.Close
2022  2.6  0.2 -1.8 -3.7 -0.5 -1.8 3.3 -1.1 -3.0 -1.5 4.9  0.2       -2.6
2023  0.9 -0.3  1.6  0.8  0.0  2.3 0.3  0.1  0.3  0.3 0.3 -0.5        6.1
2024 -1.9 -0.4 -1.1 -1.8  0.5 -1.6 1.5 -0.3  2.3 -1.8 1.0 -0.7       -4.4
2025 -0.7  1.9  1.9  0.5   NA   NA  NA   NA   NA   NA  NA   NA        3.7
     BLK.Close
2022     -10.9
2023       5.1
2024       2.2
2025       4.6
table.Stats(retornos)
                AAPL.Close BLK.Close
Observations      832.0000  832.0000
NAs                 0.0000    0.0000
Minimum            -0.0925   -0.0771
Quartile 1         -0.0088   -0.0096
Median              0.0010    0.0003
Arithmetic Mean     0.0004    0.0002
Geometric Mean      0.0002    0.0000
Quartile 3          0.0101    0.0088
Maximum             0.1533    0.1347
SE Mean             0.0006    0.0006
LCL Mean (0.95)    -0.0009   -0.0010
UCL Mean (0.95)     0.0016    0.0014
Variance            0.0003    0.0003
Stdev               0.0186    0.0177
Skewness            0.5363    0.5524
Kurtosis            7.4128    6.3950
table.DownsideRisk(retornos)
                              AAPL.Close BLK.Close
Semi Deviation                    0.0130    0.0122
Gain Deviation                    0.0135    0.0132
Loss Deviation                    0.0128    0.0117
Downside Deviation (MAR=210%)     0.0176    0.0170
Downside Deviation (Rf=0%)        0.0128    0.0121
Downside Deviation (0%)           0.0128    0.0121
Maximum Drawdown                  0.3343    0.4210
Historical VaR (95%)             -0.0295   -0.0267
Historical ES (95%)              -0.0418   -0.0381
Modified VaR (95%)               -0.0245   -0.0238
Modified ES (95%)                -0.0245   -0.0238

Los gráficos de rendimiento son muy útiles para evaluar el rendimiento de una estrategia de trading. El paquete PerformanceAnalytics ofrece una amplia variedad de gráficos, como gráficos de líneas, gráficos de dispersión, gráficos de barras y gráficos de caja.

charts.PerformanceSummary(retornos, wealth.index = TRUE, main = "Rendimiento de compra y retención")

chart.Boxplot(retornos, main = "Boxplot de retornos")

chart.Histogram(retornos, main = "Histograma de retornos", note.cex = 0.5, 
                methods = c("add.density", "add.normal", "add.risk"))

chart.RiskReturnScatter(retornos, main = "Riesgo vs Retorno", col = "blue")

Paquete PortfolioAnalytics

PortfolioAnalytics es un paquete de R que proporciona soluciones numéricas para problemas de cartera con restricciones y conjuntos de objetivos complejos. El objetivo del paquete es ayudar a profesionales e investigadores a resolver problemas de optimización de cartera con restricciones y objetivos complejos que reflejen aplicaciones del mundo real.

data(edhec)
class(edhec)
[1] "xts" "zoo"
ret_edhec <- tail(edhec, 60) # Extraer los últimos 5 años
range(index(edhec)) # Fechas de inicio y fin de `edhec`
[1] "1997-01-31" "2021-05-31"
range(index(ret_edhec)) # Fechas de inicio y fin de ret_edhec
[1] "2016-06-30" "2021-05-31"
# names(edhec) # Los nombres de `edhec` son largos, por lo que se recomienda usar nombres más cortos.
colnames(ret_edhec) <- c("CA", "CTAG", "DS", "EM", "EMN", "ED",
"FIA", "GM", "LSE", "MA", "RV", "SS", "FF")
print(head(ret_edhec, 5))
               CA    CTAG     DS     EM     EMN      ED     FIA      GM     LSE
2016-06-30 0.0016  0.0352 0.0082 0.0149 -0.0037 -0.0012 -0.0021  0.0107 -0.0093
2016-07-31 0.0154  0.0067 0.0192 0.0233  0.0080  0.0186  0.0114  0.0064  0.0203
2016-08-31 0.0102 -0.0225 0.0209 0.0169 -0.0019  0.0149  0.0076 -0.0061  0.0062
2016-09-30 0.0070 -0.0066 0.0097 0.0097  0.0040  0.0039  0.0062 -0.0028  0.0064
2016-10-31 0.0027 -0.0257 0.0203 0.0064  0.0024 -0.0015  0.0063  0.0014 -0.0072
                MA     RV      SS      FF
2016-06-30 -0.0018 0.0027  0.0173 -0.0074
2016-07-31  0.0070 0.0125 -0.0592  0.0134
2016-08-31  0.0082 0.0064 -0.0398  0.0043
2016-09-30  0.0041 0.0056 -0.0148  0.0040
2016-10-31 -0.0062 0.0052  0.0211 -0.0027
# Crear objeto de cartera
fund_edhec <- colnames(ret_edhec)
pspec_maxret <- portfolio.spec(assets = fund_edhec)
# Añadir restricciones al objeto de cartera
pspec_maxret <- add.constraint(pspec_maxret, type = "full_investment")
pspec_maxret <- add.constraint(portfolio = pspec_maxret, type = "box",
min = rep(0.05, 13),
max = c(rep(0.15, 8), rep(0.1, 5)))
# Añadir objetivo al objeto de cartera
pspec_maxret <- add.objective(portfolio = pspec_maxret,
type = "return", name = "mean")
pspec_maxret
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

Call:
portfolio.spec(assets = fund_edhec)

Number of assets: 13 
Asset Names
 [1] "CA"   "CTAG" "DS"   "EM"   "EMN"  "ED"   "FIA"  "GM"   "LSE"  "MA"  
More than 10 assets, only printing the first 10

Constraints
Enabled constraint types
        - full_investment 
        - box 

Objectives:
Enabled objective names
        - mean 
# Ejecutar la optimización con el solucionador predeterminado
opt_maxret <- optimize.portfolio(R = ret_edhec, portfolio = pspec_maxret, optimize_method = "CVXR")
opt_maxret
***********************************
PortfolioAnalytics Optimization
***********************************

Call:
optimize.portfolio(R = ret_edhec, portfolio = pspec_maxret, optimize_method = "CVXR")

Optimal Weights:
    CA   CTAG     DS     EM    EMN     ED    FIA     GM    LSE     MA     RV 
0.1298 0.0499 0.0716 0.1499 0.0499 0.1499 0.0499 0.0499 0.0999 0.0499 0.0499 
    SS     FF 
0.0499 0.0499 

Objective Measures:
    mean 
0.005173 
opt_maxret$solver
[1] "SCS"
# Ejecutar la optimización con un solucionador diferente
opt_maxret_glpk <- optimize.portfolio(R = ret_edhec, portfolio = pspec_maxret, 
                                      optimize_method = c("CVXR", "GLPK"))
opt_maxret_glpk$solver
[1] "GLPK"

Paquete portfolioBacktest

Cuando un operador diseña una estrategia de cartera, lo primero que debe hacer es realizar un backtest. El backtesting es el proceso mediante el cual se pone a prueba la estrategia de cartera utilizando los datos históricos del mercado disponibles.

Un enfoque común consiste en realizar un único backtesting con los datos históricos existentes y luego trazar gráficos para extraer conclusiones.

Esto es un grave error. Realizar un único backtesting no es representativo, ya que solo representa una conclusión, y definitivamente se sobreajustará la estrategia probada si se ajustan los parámetros o se realizan comparaciones de carteras. La sección 1 de este capítulo del libro sobre backtesting ilustra los peligros del backtesting.

El paquete portfolioBacktest realiza múltiples pruebas retrospectivas de carteras de forma automatizada y en ventanas móviles, tomando datos aleatoriamente de diferentes mercados, períodos de tiempo y universos bursátiles. A continuación, se muestra un ejemplo sencillo de uso con la cartera con ponderación equitativa:

  • Paso 1: Cargar el paquete y los conjuntos de datos (debería descargar muchos más conjuntos de datos; consulte la viñeta)
data("dataset10")
  • Paso 2: – Define tu propia cartera
my_portfolio <- function(dataset, ...) {
  prices <- dataset$adjusted
  N <- ncol(prices)
  return(rep(1/N, N))
}
  • Paso 3:Realizar una prueba retrospectiva (el conjunto de datos 10 solo contiene 10 conjuntos de datos con fines ilustrativos).
bt <- portfolioBacktest(my_portfolio, dataset10)
  • Paso 4: Verifique el rendimiento de su cartera
backtestSummary(bt)$performance 
                           fun1
Sharpe ratio       1.476203e+00
max drawdown       8.937890e-02
annual return      1.594528e-01
annual volatility  1.218623e-01
Sortino ratio      2.057677e+00
downside deviation 8.351402e-02
Sterling ratio     2.122653e+00
Omega ratio        1.295090e+00
VaR (0.95)         1.101934e-02
CVaR (0.95)        1.789425e-02
rebalancing period 1.000000e+00
turnover           8.641594e-03
ROT (bps)          7.334458e+02
cpu time           1.538462e-03
failure rate       0.000000e+00

R Scripts

Una forma sencilla de usar R es escribir los comandos uno por uno en la ventana de comandos. Sin embargo, esto se vuelve rápidamente incómodo y es necesario escribir scripts. En RStudio, se puede simplemente crear un nuevo script de R o abrir un archivo .R, donde los comandos se escriben en el mismo orden en que se ejecutarán posteriormente (este punto es fundamental).

Con el script de R abierto, se puede ejecutar línea por línea (haciendo clic en un botón o con un atajo de teclado) o generar el archivo R completo (también haciendo clic en un botón o con un atajo de teclado).

Como alternativa, también se puede generar el archivo R desde la línea de comandos con source("filename.R"), pero primero hay que asegurarse de estar en la carpeta correcta (para ver y configurar el directorio actual, se utilizan los comandos getwd() y setwd("folder_name")).

El uso del comando source("filename.R") es muy conveniente cuando uno tiene una biblioteca de funciones o datos útiles que se necesitan antes de la ejecución de otro script principal de R.

R Markdown

Otro tipo importante de scripts es el formato R Markdown (con extensión de archivo .Rmd). Es un formato extremadamente versátil que permite la combinación de texto formateable, matemáticas basadas en códigos Latex, código R (o cualquier otro lenguaje) y la posterior inclusión automática de los resultados de la ejecución del código (gráficos, tablas o cualquier otro tipo de salida).

Este tipo de formato también existe para Python y se conoce generalmente como Jupyter Notebooks. Recientemente se ha vuelto clave en el contexto de la investigación reproducible (ya que cualquiera puede ejecutar el archivo fuente .Rmd y reproducir todos los gráficos y la salida). Este documento que está leyendo es un ejemplo de un script de R Markdown.

Los archivos de R Markdown se pueden crear o abrir directamente desde RStudio. Para compilar el archivo fuente .RMD, simplemente haga clic en el botón “Knit” y se generará automáticamente un HTML tras ejecutar todos los fragmentos de código (también se pueden generar otros formatos, como PDF).

A continuación, se muestra una plantilla sencilla de encabezado/cuerpo que puede utilizarse para preparar proyectos/informes para este curso:

---
title: "Hello R Markdown"
author: "Awesome Me"
date: "2018-09-05"
output: html_document
---
# Resumen de este documento aquí.

# Primer encabezado

## Primer subencabezado

## Segundo subencabezado

# Segundo encabezado

# - Lista de viñetas 1
# - Lista de viñetas 2
# + más 2a
# + más 2b

# Este es un enlace: [Tutorial de RMarkdown](http://rmarkdown.rstudio.com)

Para obtener más información sobre el formato R Markdown:

Para explorar más

Existen varias Vistas de Tareas de CRAN relevantes para aplicaciones financieras, cada una de las cuales abarca varios paquetes: