Dashboard Financiero

R Shiny bs4Dash

Panel Financiero sobre datos del tipo de cambio en Argentina.

Maxi Galoto
2023-10-02

react

Inicio



Introducción

Volver al Inicio



Los precios en Argentina cambian con mucha frecuencia, también lo hacen los precios relativos y, a veces, con variaciones altas durante el día. Justamente este ultimo caso es para el tipo de cambio.

La relación peso dolar en Argentina se mide a través de distintos tipos de cambios como el financiero, el dolar calle (Blue) y el comercial. Para poder seguir estos precios y la brecha cambiaria entre los principales tipos de cambio desarrolle esta sencilla aplicación utilizando el framework Shiny a los efectos de poder tener una dimensión de los precios, variaciones y relaciones entre ellos.

Esta aplicación en R se desarrollo utilizando el framework bs4Dash para consultar los datos de los distintos tipos de cambio en Argentina. El Dashboard contiene la siguientes funcionalidades para interactuar con los datos de ámbito dolar:



ETL

Volver al Inicio



A continuación se muestra un ejemplo de la extracción de los datos del dolar informal desde la API de Ambito.

En primer lugar se obtienen la urls de acceso para el valor diario y el histórico:

url_informal = "https://mercados.ambito.com/dolar/informal/historico-general/"
url_informal_diario <- "https://mercados.ambito.com/dolar/informal/variacion"

Luego creamos una función que puede ser reutilizable para obtener en formato json la respuesta de la API:

get_json <- function(url) {
  tryCatch({
    response <- httr::GET(url, headers = c("sec-ch-ua" = "^Chromium^;v=^116^, ^Not"))
    status <- httr::http_status(response)$reason 
    if (status == "OK") {
      response_content <- httr::content(response, "text")
      tryCatch({
        response_json <- jsonlite::fromJSON(response_content, simplifyVector = FALSE)
        return(response_json)
      }, error = function(e) {
        cat("Error al analizar el JSON:", e$message,url, "\n")
        return(NULL)
      })
    } else {
      cat("La solicitud no se pudo completar. Código de estado:", status,url, "\n")
      return(NULL)
    }
  }, error = function(e) {
    cat("Error en la solicitud HTTP:", e$message, "\n")
    return(NULL)
  })
}

El siguiente paso es obtener los datos diarios del tipo de cambio, para ello realizamos una request a la API, transformamos el json en un tibble y por ultimo le damos el formato deseado al tibble:

format_df_informal = function(df){
  return(
    df %>% mutate(
      Fecha = as.Date(strsplit(fecha, " - ")[[1]][1], format = "%d/%m/%Y"),
      Compra = round(as.numeric(gsub(",", ".", compra)),2),
      Venta = round(as.numeric(gsub(",", ".", venta)),2),
      Promedio = ((Compra + Venta) / 2),
      variacion = round(as.numeric(gsub(",", ".", gsub("%", "", variacion))),4)) %>%
      select(Fecha, Compra, Venta, Promedio, variacion)
  )
}


informal_json_diario = format_df_informal(
  as_tibble(get_json(url_informal_diario))
  )
head(informal_json_diario)%>% gt()
Fecha Compra Venta Promedio variacion
2023-11-21 1025 1075 1050 13.16

Luego obtenemos los datos históricos utilizando un rango temporal:

get_dolar_informal = function(url, from, to){
  url_historico <- paste0(url, from,"/",to)
  response_json = get_json(url_historico)
  data_frame <- response_json %>%
    purrr::map_dfr(~setNames(as.list(.x), c("Fecha", "Compra", "Venta")))
  mi_tibble <- as_tibble(data_frame)[-1, ]
  df <- mi_tibble %>% 
    mutate(Fecha = as.Date(Fecha, format = "%d/%m/%Y")) %>%
    arrange(Fecha) %>%
    group_by(Fecha) %>%
    filter(Venta == max(Venta)) %>%
    ungroup() %>% 
    distinct()
  return(
      df %>% arrange(Fecha) %>% mutate(
        Fecha = as.Date(Fecha, format = "%d/%m/%Y"),
        Compra = round(as.numeric(gsub(",", ".", Compra)),2),
        Venta = round(as.numeric(gsub(",", ".", Venta)),2),
        Promedio = ((Compra + Venta) / 2),
        variacion = round((Promedio - lag(Promedio, n = 1)) / 
                            lag(Promedio, n = 1),4)*100) %>%
        select(Fecha, Compra, Venta, Promedio, variacion)
    )
}

today = as.Date(today())
from = as.Date(today() - years(1))
to = as.Date(today())

informal_json_anual = get_dolar_informal(url_informal, from, to) %>% 
  filter(Fecha != to, Fecha != today) %>% arrange(desc(Fecha))

head(informal_json_anual) %>% gt()
Fecha Compra Venta Promedio variacion
2023-11-17 900 950 925 0.00
2023-11-16 900 950 925 -2.12
2023-11-15 920 970 945 5.00
2023-11-14 875 925 900 0.00
2023-11-13 875 925 900 -3.74
2023-11-10 910 960 935 1.08

El ultimo paso es juntar el dataframe diario con el historico y ya tenemos los datos:

informal = rbind(
    informal_json_anual, 
    informal_json_diario
    ) %>% arrange(desc(Fecha)) %>% distinct()


head(informal) %>% gt()
Fecha Compra Venta Promedio variacion
2023-11-21 1025 1075 1050 13.16
2023-11-17 900 950 925 0.00
2023-11-16 900 950 925 -2.12
2023-11-15 920 970 945 5.00
2023-11-14 875 925 900 0.00
2023-11-13 875 925 900 -3.74



Reactividad

Volver al Inicio



Si queremos desarrollar una Shiny App y que en en la interfaz de usuario podamos ver los cambios en los precios cuando actualizamos la aplicación y que en consecuencia se modifiquen otros elementos existentes dependientes de esos precios tenemos que utilizar variables reactivas.

En Shiny, la Programacion Reactiva se utiliza para construir aplicaciones que pueden responder de manera dinámica a las interacciones del usuario y a los cambios en los datos.

En R, reactive y reactiveVal son funciones que se utilizan en el contexto de programación reactiva, especialmente en el desarrollo de aplicaciones Shiny.

reactiveVal es una función que se utiliza para crear un contenedor reactivo mutable con un valor inicial. A diferencia de reactive, que encapsula expresiones reactivas, reactiveVal encapsula un valor reactivo específico. Puede ser útil cuando se necesita almacenar y modificar un valor en respuesta a eventos sin tener que crear una función completa.

reactive se utiliza para definir objetos reactivos. Un objeto reactivo es aquel cuyo valor puede cambiar en respuesta a cambios en otros objetos reactivos o eventos externos. Los objetos reactivos se utilizan comúnmente para encapsular operaciones o cálculos que dependen de entradas dinámicas, como widgets de la interfaz de usuario (por ejemplo, un deslizador o un cuadro de texto). Se usa dentro de una función Shiny para crear un objeto reactivo. La función reactive toma como argumento una expresión R y devuelve un objeto reactivo que representa el resultado de esa expresión.

Para esta aplicación se utilizo con reactiveVal la obtención de los datos desde la API y como reactive los elementos que dependen de esa extracción de datos.

Este es un ejemplo sencillo de reactive y reactiveVal:

dolarmepcierre depende de mep, si hay un cambio en mep eso hace que la variable reactiva dolarmepcierre cambie si se pueda visualizar en la UI los cambios.


mep = reactiveVal(
  rbind(
    mep_json_anual, 
    mep_json_diario
  ) %>% arrange(Fecha) %>% distinct()
  )


dolarmepcierre = reactive(
  mep()$Promedio[nrow(mep())]
  )

Mas informacion sobre reactividad en shiny: Mastering-Shiny



Ejecución

Volver al Inicio



Para correr la app en un contenedor de Docker hay que utilizar el siguiente comando:

(Primero cambiar las source de los volumes en docker-compose.yml)

docker-compose up -d

Este comando va a crear una instancia de una imagen (un contenedor) en el puerto que se encuentra configurado en el docker-compose.yaml.

Si la imagen no esta descargada primero lo que va a hacer es descargarla y luego levantar el contenedor.

Para detener y eliminar el contenedor hay que correr:

docker-compose down



Repositorio

Volver al Inicio