Code
library(quantmod)
library(dplyr)
library(ggplot2)
library(knitr)
library(kableExtra)
library(plotly)
library(DT)Este informe analiza la evolución histórica del precio de cierre de la acción de BBVA utilizando R y Quarto. A partir de datos descargados desde Yahoo Finance, se realiza una preparación básica del conjunto de datos, un análisis descriptivo y una representación gráfica interactiva de su comportamiento en el tiempo.
En este informe se utilizan varios paquetes de R. El paquete quantmod (Quantitative Financial Modelling) permite descargar datos financieros históricos directamente desde fuentes como Yahoo Finance. El paquete dplyr, perteneciente al ecosistema tidyverse, proporciona las funciones principales para la manipulación y transformación de datos. El paquete ggplot2, también del tidyverse, es el sistema de visualización que utilizaremos para construir los gráficos. Finalmente, knitr y kableExtra permiten generar tablas formateadas dentro del informe, y plotly y DT añaden interactividad a los gráficos y tablas respectivamente.
library(quantmod)
library(dplyr)
library(ggplot2)
library(knitr)
library(kableExtra)
library(plotly)
library(DT)La descarga de datos se realiza mediante la función getSymbols() del paquete quantmod, que conecta con Yahoo Finance y almacena los datos en el entorno global como un objeto de tipo xts (serie temporal). El ticker utilizado es “BBVA.MC”, que corresponde a BBVA cotizando en el mercado continuo español (Madrid). A continuación, transformamos ese objeto en un data.frame estándar —la estructura tabular de R que hemos trabajado durante el curso— extrayendo únicamente la fecha con index(), el precio de cierre con Cl() y el volumen con Vo(). Finalmente, eliminamos los valores ausentes con na.omit() para asegurar que el conjunto de datos esté limpio antes de cualquier análisis.
getSymbols("BBVA.MC", src = "yahoo")[1] "BBVA.MC"
bbva <- data.frame(
fecha = index(BBVA.MC),
cierre = as.numeric(Cl(BBVA.MC)),
volumen = as.numeric(Vo(BBVA.MC))
) |>
na.omit()Para obtener un resumen numérico de la serie, utilizamos summarise() del paquete dplyr, una función que colapsa todas las filas del data.frame en una sola fila con los estadísticos que le indiquemos. En este caso calculamos la media del precio de cierre, el mínimo, el máximo, el último valor registrado y la rentabilidad total acumulada desde el primer dato disponible. Además, aplicamos mutate() junto con across(everything(), round, 2) para redondear todas las columnas a dos decimales de forma simultánea, una técnica vista en clase para aplicar una misma función a varias variables a la vez. El resultado se presenta en una tabla formateada con kbl() y kable_styling() del paquete kableExtra.
resumen <- bbva |>
summarise(
precio_medio = mean(cierre),
precio_minimo = min(cierre),
precio_maximo = max(cierre),
ultimo_precio = dplyr::last(cierre),
rentabilidad_total_pct = (dplyr::last(cierre) / dplyr::first(cierre) - 1) * 100
) |>
mutate(across(everything(), round, 2))
kbl(resumen, caption = "Indicadores clave de la acción de BBVA") |>
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed"))| precio_medio | precio_minimo | precio_maximo | ultimo_precio | rentabilidad_total_pct |
|---|---|---|---|---|
| 8.28 | 2.16 | 22.03 | 19.57 | 11.68 |
Una vez obtenidos los indicadores globales, resulta interesante desagregar el análisis por año. Para ello utilizamos mutate() para extraer el año de la variable fecha, y a continuación aplicamos group_by() seguido de summarise(), una combinación central del paquete dplyr que permite calcular estadísticos de forma independiente para cada grupo. En este caso agrupamos por año y calculamos el precio medio de cierre, el precio mínimo, el máximo y el volumen total negociado. El resultado se presenta con kableExtra siguiendo el mismo estilo que la tabla anterior.
bbva_anual <- bbva |>
mutate(anio = format(fecha, "%Y")) |>
group_by(anio) |>
summarise(
precio_medio = round(mean(cierre), 2),
precio_minimo = round(min(cierre), 2),
precio_maximo = round(max(cierre), 2),
volumen_total = round(sum(volumen), 0)
)
kbl(bbva_anual,
caption = "Resumen anual de la acción de BBVA",
col.names = c("Año", "Precio medio (€)", "Mínimo (€)", "Máximo (€)", "Volumen total")) |>
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover", "condensed")
)| Año | Precio medio (€) | Mínimo (€) | Máximo (€) | Volumen total |
|---|---|---|---|---|
| 2007 | 17.14 | 14.99 | 19.29 | 13419373861 |
| 2008 | 11.72 | 6.88 | 15.93 | 14233014964 |
| 2009 | 9.30 | 4.50 | 12.65 | 13167785612 |
| 2010 | 9.35 | 7.08 | 12.63 | 18120989874 |
| 2011 | 7.38 | 5.14 | 9.43 | 15568351267 |
| 2012 | 5.99 | 4.43 | 7.30 | 17597598550 |
| 2013 | 7.61 | 6.24 | 9.33 | 14164809856 |
| 2014 | 9.02 | 7.72 | 9.93 | 12423159023 |
| 2015 | 8.43 | 6.71 | 9.73 | 11943234117 |
| 2016 | 5.74 | 4.76 | 6.76 | 12422675747 |
| 2017 | 7.14 | 5.97 | 7.93 | 9106843475 |
| 2018 | 6.02 | 4.54 | 7.64 | 9049248935 |
| 2019 | 4.94 | 4.21 | 5.60 | 7829809017 |
| 2020 | 3.29 | 2.16 | 5.33 | 8777254091 |
| 2021 | 5.08 | 3.76 | 6.28 | 5861431943 |
| 2022 | 5.01 | 4.00 | 6.06 | 5899706841 |
| 2023 | 7.09 | 5.78 | 8.70 | 4204469565 |
| 2024 | 9.48 | 8.00 | 11.23 | 3015698041 |
| 2025 | 14.37 | 9.30 | 20.08 | 2624090119 |
| 2026 | 19.73 | 17.91 | 22.03 | 930341537 |
Para complementar el análisis anual, calculamos la rentabilidad de la acción en cada año. Para ello utilizamos group_by() agrupando por año y summarise() para obtener el primer y último precio de cada ejercicio, y a continuación mutate() para calcular el porcentaje de variación. Representamos el resultado con geom_col(), una variante de geom_bar() del paquete ggplot2 que permite especificar directamente el valor de cada barra. Utilizamos la variable de rentabilidad para asignar el color, de forma que los años positivos aparecen en verde y los negativos en rojo, lo que permite identificar de un vistazo los mejores y peores ejercicios bursátiles de BBVA.
bbva_rent <- bbva |>
mutate(anio = format(fecha, "%Y")) |>
group_by(anio) |>
summarise(
precio_inicio = first(cierre),
precio_fin = last(cierre)
) |>
mutate(
rentabilidad = round((precio_fin / precio_inicio - 1) * 100, 2),
color = ifelse(rentabilidad >= 0, "positivo", "negativo")
)
g3 <- ggplot(bbva_rent, aes(x = anio, y = rentabilidad, fill = color)) +
geom_col() +
scale_fill_manual(values = c("positivo" = "darkgreen", "negativo" = "firebrick")) +
geom_hline(yintercept = 0, linewidth = 0.4, color = "gray30") +
labs(
title = "Rentabilidad anual de la acción de BBVA",
subtitle = "Variación porcentual del precio de cierre entre inicio y fin de cada año",
caption = "Fuente: Yahoo Finance via quantmod",
x = "Año",
y = "Rentabilidad (%)",
fill = NULL
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none",
plot.caption = element_text(color = "gray50", size = 8)
)
ggplotly(g3)Otro indicador relevante para el análisis bursátil es la volatilidad, que mide la variabilidad del precio a lo largo del tiempo. Una volatilidad alta indica que el precio ha fluctuado mucho durante ese periodo, lo que se asocia generalmente a momentos de incertidumbre o inestabilidad en los mercados. Para calcularla utilizamos de nuevo group_by() y summarise() junto con la función sd() de R-base, que calcula la desviación típica del precio de cierre de cada año. Cuanto mayor es ese valor, más inestable fue la cotización durante ese ejercicio.
bbva_vol <- bbva |>
mutate(anio = format(fecha, "%Y")) |>
group_by(anio) |>
summarise(
volatilidad = round(sd(cierre), 2)
)
g4 <- ggplot(bbva_vol, aes(x = anio, y = volatilidad)) +
geom_col(fill = "steelblue") +
geom_hline(yintercept = mean(bbva_vol$volatilidad),
linetype = "dashed", color = "gray40", linewidth = 0.5) +
annotate("text",
x = 1,
y = mean(bbva_vol$volatilidad) + 0.15,
label = paste0("Media: ", round(mean(bbva_vol$volatilidad), 2)),
color = "gray40", size = 3, hjust = 0) +
labs(
title = "Volatilidad anual de la acción de BBVA",
subtitle = "Desviación típica del precio de cierre por año",
caption = "Fuente: Yahoo Finance via quantmod",
x = "Año",
y = "Volatilidad (€)"
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
plot.caption = element_text(color = "gray50", size = 8)
)
ggplotly(g4)La evolución del precio de BBVA no puede entenderse sin tener en cuenta el contexto económico de cada periodo. La tabla anual refleja con claridad cómo los grandes eventos macroeconómicos han impactado directamente en la cotización del banco.
La crisis financiera global de 2008 provocó el derrumbe más brusco de toda la serie, con el precio cayendo de los 19€ de 2007 hasta los 4.50€ en 2009. BBVA, como gran banco con exposición al mercado hipotecario e inmobiliario español, sufrió de lleno el impacto de la crisis de crédito global y el estallido de la burbuja inmobiliaria en España.
La crisis de deuda soberana de 2012 supuso un segundo golpe. Con la prima de riesgo española rozando los 600 puntos básicos y el rescate bancario europeo de por medio, la acción tocó su mínimo histórico de 4.43€, reflejando la desconfianza de los mercados sobre la solvencia del sistema financiero español.
La irrupción del COVID-19 en 2020 volvió a hundir la cotización hasta los 2.16€, el nivel más bajo de toda la serie histórica. La incertidumbre económica global y las moratorias hipotecarias impuestas por los gobiernos presionaron con fuerza los márgenes del sector bancario.
Sin embargo, el ciclo de subida de tipos del BCE iniciado en 2022 ha supuesto un punto de inflexión histórico para la banca europea. Los tipos más altos amplían el margen de intereses de los bancos, y BBVA ha sido uno de los grandes beneficiados: desde los 4€ de 2022 ha escalado hasta superar los 22€ en 2026, multiplicando por más de cinco su valor en apenas cuatro años. A esto se suma el impacto positivo en la cotización de la operación de adquisición del Banco Sabadell, que ha reforzado las expectativas de crecimiento del grupo.
La visualización se construye con ggplot2 usando geom_line() para trazar la evolución del precio. Para enriquecer el gráfico con contexto histórico, añadimos líneas verticales con geom_vline() en los momentos clave identificados en la sección anterior: la crisis financiera de 2008, la crisis de deuda soberana de 2012, el COVID-19 en 2020 y el inicio de la subida de tipos del BCE en 2022. Las etiquetas de cada evento se añaden con geom_text(), y se mantienen las referencias al mínimo y máximo histórico con geom_hline() y annotate(). Finalmente, labs() completa el gráfico con título, subtítulo y fuente, y ggplotly() lo convierte en interactivo.
precio_min <- min(bbva$cierre)
precio_max <- max(bbva$cierre)
fecha_min <- bbva$fecha[which.min(bbva$cierre)]
fecha_max <- bbva$fecha[which.max(bbva$cierre)]
eventos <- data.frame(
fecha = as.Date(c("2008-09-15", "2012-06-09", "2020-03-16", "2022-07-21")),
label = c("Crisis\nfinanciera", "Crisis deuda\nsoberana", "COVID-19", "Subida\ntipos BCE"),
color = c("firebrick", "firebrick", "firebrick", "darkgreen")
)
g1 <- ggplot(bbva, aes(x = fecha, y = cierre)) +
geom_line(color = "darkblue", linewidth = 0.7) +
geom_vline(data = eventos,
aes(xintercept = fecha),
linetype = "dashed",
color = eventos$color,
linewidth = 0.5) +
geom_text(data = eventos,
aes(x = fecha, y = max(bbva$cierre) * 0.85, label = label),
size = 2.8,
color = eventos$color,
hjust = -0.1,
lineheight = 0.85) +
geom_hline(yintercept = precio_min, linetype = "dotted",
color = "gray50", linewidth = 0.4) +
geom_hline(yintercept = precio_max, linetype = "dotted",
color = "gray50", linewidth = 0.4) +
annotate("text", x = min(bbva$fecha), y = precio_min + 0.6,
label = paste0("Mín: ", round(precio_min, 2), "€"),
color = "gray40", size = 2.8, hjust = 0) +
annotate("text", x = min(bbva$fecha), y = precio_max + 0.6,
label = paste0("Máx: ", round(precio_max, 2), "€"),
color = "gray40", size = 2.8, hjust = 0) +
labs(
title = "Evolución del precio de cierre de BBVA",
subtitle = paste0("Periodo: ", format(min(bbva$fecha), "%Y"),
" – ", format(max(bbva$fecha), "%Y")),
caption = "Fuente: Yahoo Finance via quantmod",
x = "Fecha",
y = "Precio de cierre (€)"
) +
theme_minimal() +
theme(plot.caption = element_text(color = "gray50", size = 8))
ggplotly(g1)De forma análoga al gráfico anterior, representamos la evolución del volumen negociado diario. El volumen es un indicador financiero que mide la cantidad de acciones que cambian de manos en una sesión, y su análisis conjunto con el precio permite identificar momentos de especial actividad en el mercado. Se aplica la misma estructura de ggplot2 con geom_line() y se convierte a interactivo con ggplotly().
g2 <- ggplot(bbva, aes(x = fecha, y = volumen)) +
geom_line(color = "green", linewidth = 0.5) +
labs(
title = "Evolución del volumen negociado",
x = "Fecha",
y = "Volumen"
) +
theme_minimal()
ggplotly(g2)Para facilitar la consulta de los datos en bruto, generamos una tabla dinámica con la función datatable() del paquete DT. Antes de presentarla, renombramos las columnas con rename() de dplyr para que los nombres sean más descriptivos, y convertimos la fecha a texto con as.character() para evitar problemas de formato en la tabla. DT::datatable() permite al lector paginar, ordenar y hacer scroll sobre los datos directamente desde el documento HTML.
tabla_bbva <- bbva |>
mutate(fecha = as.character(fecha)) |>
rename(
Fecha = fecha,
`Precio de cierre` = cierre,
Volumen = volumen
)
datatable(
tabla_bbva,
caption = "Tabla dinámica de la acción de BBVA",
options = list(
pageLength = 10,
autoWidth = TRUE,
scrollX = TRUE
),
rownames = FALSE
)El conjunto de datos contiene 4929 observaciones diarias, con precios registrados desde el 01 de January de 2007 hasta el 16 de April de 2026.
A lo largo de este periodo, el precio de cierre de BBVA ha oscilado entre un mínimo de 2.16€ y un máximo de 22.03€, con un precio medio de 8.28€. La rentabilidad total acumulada en el periodo completo ha sido del 11.68%.
El análisis de rentabilidad anual muestra que los peores ejercicios coinciden exactamente con los grandes eventos macroeconómicos identificados en el contexto histórico: 2008, 2012 y 2020. Por el contrario, los años de recuperación muestran rentabilidades muy positivas, especialmente el periodo 2023-2026 impulsado por la subida de tipos del BCE.
El análisis de volatilidad confirma esta lectura: los años de mayor inestabilidad en el precio coinciden con los periodos de crisis, mientras que la tendencia alcista reciente viene acompañada de una volatilidad más contenida, señal de una recuperación sólida y sostenida.
Este trabajo ha recorrido las principales etapas del proceso de ciencia de datos introducido en el curso: la importación de datos desde una fuente externa (Yahoo Finance via quantmod), la preparación y limpieza del conjunto de datos transformándolo en un data.frame estructurado, el análisis descriptivo con funciones del tidyverse como summarise() y mutate(), la visualización mediante ggplot2 con gráficos interactivos gracias a plotly, y finalmente la presentación de resultados en forma de informe reproducible generado con Quarto.
Más allá de las herramientas técnicas, el análisis de la acción de BBVA ha permitido conectar los datos con la realidad económica: la crisis financiera de 2008, la crisis de deuda soberana de 2012, el impacto del COVID-19 en 2020 y la recuperación impulsada por la subida de tipos del BCE desde 2022 quedan reflejados con claridad tanto en el gráfico de evolución del precio como en los análisis de rentabilidad y volatilidad anual.
El informe es completamente reproducible: cualquier persona con R y los paquetes instalados puede ejecutarlo y obtener los datos actualizados automáticamente desde Yahoo Finance en el momento del render.