Análisis de la acción de BBVA

Author

Danel Clemente

Published

April 17, 2026

1 Introducción

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.

2 Carga de librerías

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.

Code
library(quantmod)
library(dplyr)
library(ggplot2)
library(knitr)
library(kableExtra)
library(plotly)
library(DT)

3 Descarga y preparación de datos

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.

Code
getSymbols("BBVA.MC", src = "yahoo")
[1] "BBVA.MC"
Code
bbva <- data.frame(
  fecha = index(BBVA.MC),
  cierre = as.numeric(Cl(BBVA.MC)),
  volumen = as.numeric(Vo(BBVA.MC))
) |>
  na.omit()

4 Análisis descriptivo

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.

Code
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"))
Indicadores clave de la acción de BBVA
precio_medio precio_minimo precio_maximo ultimo_precio rentabilidad_total_pct
8.28 2.16 22.03 19.57 11.68

5 Análisis anual

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.

Code
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")
  )
Resumen anual de la acción de BBVA
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

6 Rentabilidad anual

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.

Code
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)

7 Volatilidad anual

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.

Code
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)

8 Contexto histórico

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.

9 Gráfico de la evolución del precio

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.

Code
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)

10 Evolución del volumen de negocio

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().

Code
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)

11 Tabla dinámica de datos

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.

Code
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
)

12 Interpretación

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.

13 Conclusión

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.