Análisis Dinámico de Acciones del Índice Financiero de la Bolsa de Valores de Caracas: 2023-2025

Autor/a

Javier Melendez

Fecha de publicación

17 de febrero de 2025


Resumen

Este proyecto analizará el comportamiento de las acciones del índice financiero de la Bolsa de Valores de Caracas (BVC) entre enero de 2023 y febrero de 2024. El objetivo es ofrecer una herramienta útil para inversionistas y entender mejor las dinámicas recientes del mercado de valores venezolano

Introducción

El mercado de valores venezolano ha experimentado fluctuaciones significativas en los últimos años, influenciado por factores macroeconómicos y eventos globales. Este análisis se centra en el comportamiento de las acciones del índice financiero de la BVC, focalizándose en el período posterior a 2021, para capturar la dinámica más reciente sin el efecto residual de la pandemia.

Plantiamiento del problema

¿Cuáles son las tendencias y patrones en el comportamiento de las acciones del índice financiero de la BVC durante el período comprendido entre el 2 de enero de 2023 y el 14 de febrero de 2025?

Objetivo general

Analizar el comportamiento de las acciones del índice financiero de la BVCaracas durante el período 2023-2024

Objetivos específicos

1. Recopilar y procesar datos históricos del índice financiero de la BVC y de las acciones que lo componen.

2. Identificar tendencias, patrones y relaciones entre las variables relevantes utilizando técnicas de análisis estadístico y econométrico.

Metodologia

1. Recolección de Datos

Se trabajará con los siguientes símbolos de acciones:

“BPV”, “BNC”, “BVCC”, “ABC.A”, “MVZ.A”, “MVZ.B”

1.1. Librerías Necesarias

En primer lugar, se cargan las librerías que permiten realizar solicitudes HTTP, manejar JSON, procesar fechas y manipular datos:

Código
library(httr)       # Para realizar solicitudes HTTP
library(jsonlite)   # Para procesar datos en formato JSON
library(lubridate)  # Para el manejo de fechas

Attaching package: 'lubridate'
The following objects are masked from 'package:base':

    date, intersect, setdiff, union
Código
library(dplyr)      # Para la manipulación de datos

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
Código
library(purrr)      # Para aplicar funciones a listas y data frames

Attaching package: 'purrr'
The following object is masked from 'package:jsonlite':

    flatten
Código
library(DT)         # Para visualización interactiva de tablas

1.2. Extracción de Datos “Desnudos” desde la BVC

La siguiente función se encarga de realizar una solicitud POST a la página de la BVC y extraer los datos históricos de cada acción. Se incluyen controles de error y mensajes informativos para detectar posibles problemas en la solicitud.

Código
obtener_datos_desnudos <- function(simbolo) {
  tryCatch({
    # Configurar solicitud HTT para obtener datos desde la API de la BVC
    headers <- add_headers(
      "User-Agent" = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
      "Referer" = "https://www.bolsadecaracas.com/historicos/"
    )
    
    # Realizar solicitud POST
    response <- POST(
      "https://www.bolsadecaracas.com/wp-admin/admin-ajax.php", 
      headers,
      body = list(action = "getHistoricoSimbolo", simbolo = simbolo),
      encode = "form",
      timeout(15)
    )
    
    # Verificar que la solicitud fue exitosa
    if (status_code(response) != 200) {
      message("Error en la respuesta para ", simbolo, ": ", status_code(response))
      return(NULL)
    }
    
    # Procesar JSON y devolver el contenido completo
    contenido <- content(response, "text", encoding = "UTF-8")
    datos_json <- fromJSON(contenido)
    return(datos_json)
    
  }, error = function(e) {
    message("Error procesando ", simbolo, ": ", e$message)
    return(NULL)
  })
}

1.3. Extracción de Datos para Cada Símbolo

Se define la lista de símbolos y se recorre uno a uno para extraer sus datos. Se introduce una pausa entre solicitudes para no sobrecargar el servidor.

Código
# Lista de símbolos
simbolos <- c("BPV", "BNC", "BVCC", "ABC.A", "MVZ.A", "MVZ.B")

# Ejecución principal: recoger todos los datos sin limpieza
datos_finales <- list()

for (simbolo in simbolos) {
  message("Procesando: ", simbolo)
  datos <- obtener_datos_desnudos(simbolo)
  
  if (!is.null(datos)) {
    datos_finales[[simbolo]] <- datos
    Sys.sleep(1.5)  # Pausa para evitar saturar el servidor
  }
}
Procesando: BPV
Procesando: BNC
Procesando: BVCC
Procesando: ABC.A
Procesando: MVZ.A
Procesando: MVZ.B

2. Optimización y Limpieza del DataFrame

En esta sección se combinan los datos extraídos, se renombrar y limpian las columnas:

Conversión de fechas: Se transforma la columna FECHA al formato Date. Identificación de acción: Se añade una columna ACCION para identificar el símbolo correspondiente. Limpieza de datos numéricos: Se sustituyen comas por puntos para permitir la conversión correcta a tipo numérico.

Código
datos_totales <- map_df(names(datos_finales), function(simbolo) {
  # Extraer el componente relevante
  df <- datos_finales[[simbolo]]$cur_hist_mov_emisora
  if (is.null(df)) {
    return(data.frame(ACCION = simbolo))
  }
  
  df <- as.data.frame(df)
  
  # Renombrar columnas según el formato requerido
  colnames(df) <- c("FECHA", "PRECIO_APERT", "PRECIO_CIE", "VAR_ABS", "VAR_REL", 
                    "PRECIO_MAX", "PRECIO_MIN", "N_OPERACIONES", 
                    "TITULOS_NEGOCIADOS", "MONTO_EFECTIVO")[1:ncol(df)]
  
  # Convertir la columna FECHA al formato Date (formato original: 'dd-mm-yy')
  df$FECHA <- as_date(df$FECHA, format = '%d-%m-%y')
  
  # Agregar la columna ACCION
  df$ACCION <- simbolo
  
  return(df)
}) %>%
  # Limpiar columnas numéricas: reemplazar puntos y comas para que sean interpretadas como números
  mutate(across(
    c(PRECIO_APERT, PRECIO_CIE, VAR_ABS, VAR_REL, 
      PRECIO_MAX, PRECIO_MIN, N_OPERACIONES, 
      TITULOS_NEGOCIADOS, MONTO_EFECTIVO),
    ~ as.numeric(gsub(",", ".", gsub("\\.", "", .)))
  ))

# Verificar la estructura final del DataFrame
str(datos_totales)
'data.frame':   6588 obs. of  11 variables:
 $ FECHA             : Date, format: "2025-02-17" "2025-02-14" ...
 $ PRECIO_APERT      : num  4.14 4.1 3.85 3.85 3.8 3.9 3.9 4.35 4 3.55 ...
 $ PRECIO_CIE        : num  4.21 4.14 4.1 3.85 3.85 3.8 3.9 3.9 4.35 4 ...
 $ VAR_ABS           : num  0.07 0.04 0.25 0 0.05 -0.1 0 -0.45 0.35 0.45 ...
 $ VAR_REL           : num  1.66 0.97 6.1 0 1.3 ...
 $ PRECIO_MAX        : num  4.23 4.3 4.14 4 3.88 3.89 4 4.34 4.35 4 ...
 $ PRECIO_MIN        : num  4.14 4.1 3.85 3.85 3.8 3.79 3.8 3.9 4.05 3.55 ...
 $ N_OPERACIONES     : num  61 47 52 38 60 54 59 39 66 84 ...
 $ TITULOS_NEGOCIADOS: num  9797 21894 48745 365637 54700 ...
 $ MONTO_EFECTIVO    : num  41302 91524 196334 1407897 208731 ...
 $ ACCION            : chr  "BPV" "BPV" "BPV" "BPV" ...

2.2Visualización de la Tabla Completa

Se utiliza la función datatable() del paquete DT para crear una tabla interactiva del conjunto de datos_totales.

Código
datatable(datos_totales,
          rownames = FALSE,
          # No incluimos la extensión 'Buttons' ni configuramos botones de descarga
          options = list(
            dom = 'frtip',  # 'f' = filtro, 'r' = procesamiento, 't' = tabla, 'i' = info, 'p' = paginación
            lengthChange = FALSE,
            language = list(url = '//cdn.datatables.net/plug-ins/1.10.11/i18n/Spanish.json'),
            pageLength = 5,
            initComplete = JS(
              "function(settings, json) {",
              "$(this.api().table().header()).css({'background-color': '#224eb2','color': '#fff'});",
              "}"
            ),
            Filter = 0
          ),
          escape = FALSE
)# %>% 
Código
 # formatStyle(columns = colnames(.), fontSize = '50%')

3. Filtrado de Datos por Rango de Fechas

Se filtran los datos para conservar únicamente el período comprendido entre el 1 de enero de 2023 y el 14 de febrero de 2025.

Código
# Definir el rango de fechas
inicio <- as.Date("2023-01-01")
fin    <- as.Date("2025-02-11")  # Incluye febrero 2025

# Filtrar el DataFrame para el rango deseado
datos_filtrados <- datos_totales %>% 
  filter(FECHA >= inicio & FECHA <= fin)

# Visualizar las primeras filas del conjunto filtrado
head(datos_filtrados)
       FECHA PRECIO_APERT PRECIO_CIE VAR_ABS VAR_REL PRECIO_MAX PRECIO_MIN
1 2025-02-11         3.80       3.85    0.05    1.30       3.88       3.80
2 2025-02-10         3.90       3.80   -0.10   -2.63       3.89       3.79
3 2025-02-07         3.90       3.90    0.00    0.00       4.00       3.80
4 2025-02-06         4.35       3.90   -0.45  -11.54       4.34       3.90
5 2025-02-05         4.00       4.35    0.35    8.05       4.35       4.05
6 2025-02-04         3.55       4.00    0.45   11.25       4.00       3.55
  N_OPERACIONES TITULOS_NEGOCIADOS MONTO_EFECTIVO ACCION
1            60              54700      208731.09    BPV
2            54              18586       70787.24    BPV
3            59              36261      141295.01    BPV
4            39              11980       49895.35    BPV
5            66              31491      133724.62    BPV
6            84              67060      261178.24    BPV

Visualización de tabla Interactiva del DataFrame datos_filtrado

Código
datatable(datos_filtrados,
          rownames = FALSE,
          #
          options = list(
            dom = 'frtip',  # 'f' = filtro, 'r' = procesamiento, 't' = tabla, 'i' = info, 'p' = paginación
            lengthChange = FALSE,
            language = list(url = '//cdn.datatables.net/plug-ins/1.10.11/i18n/Spanish.json'),
            pageLength = 5,
            initComplete = JS(
              "function(settings, json) {",
              "$(this.api().table().header()).css({'background-color': '#224eb2','color': '#fff'});",
              "}"
            ),
            Filter = 0
          ),
          escape = FALSE
) %>% 
  formatStyle(columns = colnames(.), fontSize = '50%')

4. Visualización de Gráficos

Se realizan dos tipos de visualizaciones para explorar la evolución de las acciones:

4.1. Gráficas Independientes por Acción

En este bloque se crea un gráfico interactivo con plotly para visualizar, de forma individual, la evolución del precio de cierre de cada acción. Se configura un menú que permite cambiar la traza visible y se personaliza el texto que aparece en el hover.

Código
# Cargar librerías necesarias
library(plotly)
Loading required package: ggplot2

Attaching package: 'plotly'
The following object is masked from 'package:ggplot2':

    last_plot
The following object is masked from 'package:httr':

    config
The following object is masked from 'package:stats':

    filter
The following object is masked from 'package:graphics':

    layout
Código
library(dplyr)
library(scales)

Attaching package: 'scales'
The following object is masked from 'package:purrr':

    discard
Código
# Lista única de acciones
acciones <- unique(datos_filtrados$ACCION)

# Crear una lista para almacenar la información de cada traza
trazas <- list()
for (accion in acciones) {
  datos_accion <- datos_filtrados %>% filter(ACCION == accion)
  
  # Definir el texto a mostrar en el hover (tooltip)
  hover_text <- paste("Fecha:", datos_accion$FECHA,
                      "<br>Precio Cierre:", dollar(datos_accion$PRECIO_CIE, prefix = "bs"),
                      "<br>N_OPERACIONES:", datos_accion$N_OPERACIONES,
                      "<br>TITULOS_NEGOCIADOS:", datos_accion$TITULOS_NEGOCIADOS,
                      "<br>MONTO_EFECTIVO:", datos_accion$MONTO_EFECTIVO)
  
  trazas[[accion]] <- list(
    x = datos_accion$FECHA,
    y = datos_accion$PRECIO_CIE,
    type = 'scatter',
    mode = 'lines',
    name = accion,
    line = list(width = 1.5),
    text = hover_text,
    hoverinfo = "text"  # Se indica que se usará el texto personalizado en el hover
  )
}

# Definir la visibilidad inicial: solo se muestra la primera acción
visibilidad_inicial <- sapply(acciones, function(x) x == acciones[1])

# Crear los botones del menú superior
botones <- lapply(seq_along(acciones), function(i) {
  # Vector de visibilidad: solo la traza i es visible
  vis <- rep(FALSE, length(acciones))
  vis[i] <- TRUE
  list(
    method = "update",
    args = list(
      list(visible = vis),  # Actualiza la visibilidad de las trazas
      list(title = paste("Evolución del Precio de Cierre de", acciones[i]))
    ),
    label = acciones[i]
  )
})

# Construir el gráfico
fig <- plot_ly()
for (i in seq_along(acciones)) {
  accion <- acciones[i]
  fig <- fig %>% add_trace(
    x = trazas[[accion]]$x,
    y = trazas[[accion]]$y,
    type = trazas[[accion]]$type,
    mode = trazas[[accion]]$mode,
    name = trazas[[accion]]$name,
    line = trazas[[accion]]$line,
    text = trazas[[accion]]$text,
    hoverinfo = trazas[[accion]]$hoverinfo,
    visible = visibilidad_inicial[i]
  )
}

# Configurar el layout del gráfico (incluye menú de actualización y rangos)
fig <- fig %>% layout(
  title = paste("Evolución del Precio de Cierre de", acciones[1]),
  updatemenus = list(
    list(
      type = "buttons",
      direction = "right",
      x = 0.5,  # Ajusta la posición horizontal según convenga
      y = 1.15, # Ajusta la posición vertical (por encima del gráfico)
      showactive = TRUE,
      buttons = botones
    )
  ),
  xaxis = list(
    title = "Fecha",
    rangeselector = list(
      buttons = list(
        list(count = 7, label = "1 Semana", step = "day", stepmode = "backward"),
        list(count = 1, label = "1 Mes", step = "month", stepmode = "backward"),
        list(count = 6, label = "6 Meses", step = "month", stepmode = "backward"),
        list(count = 1, label = "1 Año", step = "year", stepmode = "backward"),
        list(step = "all", label = "Todo")
      )
    ),
    rangeslider = list(visible = TRUE)
  ),
  yaxis = list(title = "Precio de Cierre (Bs)")
)

# Mostrar el gráfico
fig

4.2 Gráfica Combinada: En este gráfico se agrupa la evolución de todas las acciones. Se utiliza facet_wrap para crear paneles independientes para cada acción, permitiendo que la escala y se ajuste de manera individual para que el precio se lea de forma óptima.

Código
# Cargar librerías necesarias
library(plotly)
library(dplyr)
library(scales)

# Supongamos que 'datos_filtrados' es tu data frame con las columnas mencionadas.

# Crear el gráfico compartido
fig <- plot_ly()

for (accion in unique(datos_filtrados$ACCION)) {
  datos_accion <- datos_filtrados %>% filter(ACCION == accion)
  
  # Crear el texto para el tooltip
  hover_text <- paste("Fecha:", datos_accion$FECHA,
                      "<br>Precio Cierre:", dollar(datos_accion$PRECIO_CIE, prefix = "$"),
                      "<br>N_OPERACIONES:", datos_accion$N_OPERACIONES,
                      "<br>TITULOS_NEGOCIADOS:", datos_accion$TITULOS_NEGOCIADOS,
                      "<br>MONTO_EFECTIVO:", datos_accion$MONTO_EFECTIVO)
  
  fig <- fig %>% add_trace(
    x = datos_accion$FECHA,
    y = datos_accion$PRECIO_CIE,
    type = 'scatter',
    mode = 'lines',
    name = accion,
    line = list(width = 2),
    text = hover_text,
    hoverinfo = "text"
  )
}

# Configurar el layout del gráfico
fig <- fig %>% layout(
  title = "Evolución del Precio de Cierre - Acciones del Índice Financiero de Caracas",
  xaxis = list(
    title = "Fecha",
    rangeselector = list(
      buttons = list(
        list(count = 7, label = "1 Semana", step = "day", stepmode = "backward"),
        list(count = 1, label = "1 Mes", step = "month", stepmode = "backward"),
        list(count = 6, label = "6 Meses", step = "month", stepmode = "backward"),
        list(count = 1, label = "1 Año", step = "year", stepmode = "backward"),
        list(step = "all", label = "Todo")
      )
    ),
    rangeslider = list(visible = TRUE)
  ),
  yaxis = list(title = "Precio de Cierre (Bs)"),
  legend = list(
    orientation = 'h',  # Leyenda horizontal
    x = 0.5,
    xanchor = 'center',
    y = 1.15
  )
)

# Mostrar el gráfico
fig

5. Análisis del Volumen Negociado

Se analiza el volumen de negociación (títulos negociados) por año y acción, determinando cuál es la acción con mayor volumen negociado cada año y se genera un gráfico comparativo.

5.1. Cálculo del Volumen Anual

Código
library(dplyr)
library(lubridate)

# Agregar la columna 'Año' a partir de 'FECHA'
datos_filtrados <- datos_filtrados %>%
  mutate(Año = year(FECHA))

# Resumen anual: total negociado por cada acción en cada año
volumen_anual <- datos_filtrados %>%
  group_by(ACCION, Año) %>%
  summarise(total_vol = sum(TITULOS_NEGOCIADOS, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'ACCION'. You can override using the
`.groups` argument.

5.2. Identificación de la Acción con Mayor Volumen por Año

Código
accion_favorita_anual <- volumen_anual %>%
  group_by(Año) %>%
  filter(total_vol == max(total_vol)) %>%  # Selecciona la acción con el mayor volumen en cada año
  arrange(Año) %>%
  ungroup()

# Mostrar la acción favorita por año
print(accion_favorita_anual)
# A tibble: 3 × 3
  ACCION   Año total_vol
  <chr>  <dbl>     <dbl>
1 BNC     2023 480023500
2 BNC     2024 184172700
3 BNC     2025  11071104

5.3. Gráfico Comparativo del Volumen Negociado

Código
library(plotly)

fig_bar <- plot_ly(volumen_anual, 
                   x = ~Año, 
                   y = ~total_vol, 
                   color = ~ACCION, 
                   type = 'bar') %>%
  layout(title = "Comparativa del Volumen Negociado por Año y Acción",
         xaxis = list(title = "Año"),
         yaxis = list(title = "Total de Títulos Negociados"),
         barmode = 'group',
         legend = list(title = list(text = "<b>Acción</b>")))
fig_bar

6. Cálculo de Indicadores de Desempeño

Se calculan métricas clave utilizando el paquete PerformanceAnalytics. En primer lugar, se calcula el rendimiento diario a partir del precio de cierre y, posteriormente, se obtiene el rendimiento acumulado. Finalmente, se calculan indicadores de desempeño como el rendimiento anualizado, la volatilidad anualizada y el máximo drawdown.

6.1. Cálculo del Rendimiento Diario y Acumulado

Código
library(dplyr)
library(lubridate)

# Aseguramos que los datos estén ordenados por acción y fecha
datos_filtrados <- datos_filtrados %>%
  arrange(ACCION, FECHA)

# Calcular el rendimiento diario usando el precio de cierre.
# Rendimiento diario = (Precio actual / Precio anterior) - 1
datos_filtrados <- datos_filtrados %>%
  group_by(ACCION) %>%
  arrange(FECHA) %>%
  mutate(rend_diario = PRECIO_CIE / lag(PRECIO_CIE) - 1) %>%
  ungroup()

# Calcular el rendimiento acumulado (rendimiento total a lo largo del tiempo)
datos_filtrados <- datos_filtrados %>%
  group_by(ACCION) %>%
  arrange(FECHA) %>%
  mutate(rend_acumulado = cumprod(1 + coalesce(rend_diario, 0)) - 1) %>%
  ungroup()

6.2 Cálculo de Indicadores de Desempeño

Con el paquete PerformanceAnalytics podemos calcular métricas importantes. Para cada acción creamos un objeto xts a partir del rendimiento diario y luego calculamos:

Rendimiento Anualizado: El promedio geométrico anualizado.

Volatilidad Anualizada: La desviación estándar anualizada.

Máximo Drawdown: La mayor caída desde un pico hasta un mínimo durante el período.

Código
library(PerformanceAnalytics)
Loading required package: xts
Loading required package: zoo

Attaching package: 'zoo'
The following objects are masked from 'package:base':

    as.Date, as.Date.numeric

######################### Warning from 'xts' package ##########################
#                                                                             #
# The dplyr lag() function breaks how base R's lag() function is supposed to  #
# work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
# source() into this session won't work correctly.                            #
#                                                                             #
# Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
# conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
# dplyr from breaking base R's lag() function.                                #
#                                                                             #
# Code in packages is not affected. It's protected by R's namespace mechanism #
# Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
#                                                                             #
###############################################################################

Attaching package: 'xts'
The following objects are masked from 'package:dplyr':

    first, last

Attaching package: 'PerformanceAnalytics'
The following object is masked from 'package:graphics':

    legend
Código
library(xts)
library(dplyr)

# Asegurar que FECHA es tipo Date
datos_filtrados <- datos_filtrados %>%
  mutate(FECHA = as.Date(FECHA))

# Calcular rendimientos diarios si no están en el dataset
datos_filtrados <- datos_filtrados %>%
  group_by(ACCION) %>%
  arrange(FECHA) %>%
  mutate(rend_diario = (PRECIO_CIE / lag(PRECIO_CIE)) - 1) %>%
  ungroup()

# Función para calcular métricas financieras
calcular_metricas <- function(df) {
  serie_xts <- xts(df$rend_diario, order.by = df$FECHA)

  # Si la serie está vacía o tiene solo un dato, evitar errores
  if (nrow(serie_xts) < 2) {
    return(data.frame(
      annualized_return = NA,
      annualized_volatility = NA,
      max_drawdown = NA
    ))
  }

  data.frame(
    annualized_return = as.numeric(Return.annualized(na.omit(serie_xts), scale = 252)),
    annualized_volatility = as.numeric(StdDev.annualized(na.omit(serie_xts), scale = 252)),
    max_drawdown = as.numeric(maxDrawdown(na.omit(serie_xts)))
  )
}

# Aplicar la función para cada acción usando group_modify()
performance_metrics <- datos_filtrados %>%
  group_by(ACCION) %>%
  group_modify(~ calcular_metricas(.)) %>%
  ungroup()

# Mostrar resultados
print(performance_metrics)
# A tibble: 6 × 4
  ACCION annualized_return annualized_volatility max_drawdown
  <chr>              <dbl>                 <dbl>        <dbl>
1 ABC.A              1.65                  0.855        0.597
2 BNC               20.3                 380.           0.978
3 BPV               -0.261                 0.903        0.846
4 BVCC               2.48                  0.853        0.412
5 MVZ.A              2.22                  0.652        0.428
6 MVZ.B              3.17                  0.753        0.322

6.3. Gráfico Interactivo del Rendimiento Acumulado

Se genera un gráfico interactivo para visualizar la evolución del rendimiento acumulado de cada acción.

Código
# Cargar librerías necesarias
library(plotly)

# Crear el gráfico interactivo del rendimiento acumulado
fig_rendimiento_acumulado <- plot_ly()

# Obtener la lista de acciones
acciones <- unique(datos_filtrados$ACCION)

# Agregar trazas para cada acción
for (accion in acciones) {
  datos_accion <- datos_filtrados %>% filter(ACCION == accion)
  
  fig_rendimiento_acumulado <- fig_rendimiento_acumulado %>% add_trace(
    x = datos_accion$FECHA,
    y = datos_accion$rend_acumulado,
    type = 'scatter',
    mode = 'lines',
    name = accion,
    line = list(width = 2)
  )
}

# Configurar el layout del gráfico
fig_rendimiento_acumulado <- fig_rendimiento_acumulado %>% layout(
  title = "Evolución del Rendimiento Acumulado de las Acciones",
  xaxis = list(title = "Fecha"),
  yaxis = list(title = "Rendimiento Acumulado"),
  legend = list(title = list(text = "<b>Acción</b>"))
)

# Mostrar el gráfico
fig_rendimiento_acumulado

En el gráfico se ilustra la rentabilidad anualizada, la volatilidad anualizada y el máximo drawdown de seis acciones: ABC.A, BNC, BPV, BVCC, MVZ.A y MVZ.B. Observamos que, aunque BNC presenta una rentabilidad anualizada notablemente alta del 20.25%, también exhibe una volatilidad anualizada extremadamente elevada (379.66%) y un máximo drawdown del 97.84%, indicando un perfil de riesgo considerable. En contraste, MVZ.B muestra una rentabilidad anualizada más modesta del 3.17%, acompañada de una volatilidad anualizada del 0.75% y un máximo drawdown del 32.20%, sugiriendo una inversión potencialmente más estable. Este análisis destaca la importancia de evaluar conjuntamente la rentabilidad y las métricas de riesgo al considerar decisiones de inversión

Guardar datos

Código
# Guardar el data frame 'datos_totales' como CSV
write.csv(datos_totales, file = "datos_totales.csv", row.names = FALSE)

# Guardar el data frame 'datos_filtrados' como CSV
write.csv(datos_filtrados, file = "datos_filtrados.csv", row.names = FALSE)

# Guardar el resumen anual del volumen negociado
write.csv(volumen_anual, file = "volumen_anual.csv", row.names = FALSE)

# Guardar la acción favorita por año
write.csv(accion_favorita_anual, file = "accion_favorita_anual.csv", row.names = FALSE)

# Guardar las métricas de desempeño financieras
write.csv(performance_metrics, file = "performance_metrics.csv", row.names = FALSE)

Guardar Graficas

Código
# Exportar el gráfico interactivo del precio de cierre por acción
htmlwidgets::saveWidget(fig, "grafico_precio_cierre.html")
#
htmlwidgets::saveWidget(fig, "grafico_precio_cierre_combinado.html")
# Exportar el gráfico comparativo del volumen negociado
htmlwidgets::saveWidget(fig_bar, "grafico_volumen_negociado.html")

# Exportar el gráfico interactivo del rendimiento acumulado
htmlwidgets::saveWidget(fig_rendimiento_acumulado, "grafico_rendimiento_acumulado.html")